File: abi-aarch64.cpp

package info (click to toggle)
ldc 1%3A1.30.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 59,248 kB
  • sloc: cpp: 61,598; ansic: 14,545; sh: 1,014; makefile: 972; asm: 510; objc: 135; exp: 48; python: 12
file content (186 lines) | stat: -rw-r--r-- 5,737 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
//===-- abi-aarch64.cpp ---------------------------------------------------===//
//
//                         LDC – the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// The AArch64 Procedure Call Standard (AAPCS64) can be found here:
// https://github.com/ARM-software/abi-aa/blob/master/aapcs64/aapcs64.rst
//
//===----------------------------------------------------------------------===//

#include "gen/abi-aarch64.h"

#include "dmd/identifier.h"
#include "dmd/nspace.h"
#include "gen/abi.h"
#include "gen/abi-generic.h"

/**
 * AAPCS64 uses a special native va_list type, a struct aliased as
 * object.__va_list in druntime. Apple diverges and uses a simple char*
 * pointer.
 * __va_list arguments are never passed by value, only by reference (even though
 * the mangled function name indicates otherwise!). This requires a little bit
 * of compiler magic in the following implementations.
 */
struct AArch64TargetABI : TargetABI {
private:
  const bool isDarwin;
  IndirectByvalRewrite indirectByvalRewrite;
  ArgTypesRewrite argTypesRewrite;

  bool isAAPCS64VaList(Type *t) {
    if (isDarwin)
      return false;

    // look for a __va_list struct in a `std` C++ namespace
    if (auto ts = t->isTypeStruct()) {
      auto sd = ts->sym;
      if (strcmp(sd->ident->toChars(), "__va_list") == 0) {
        if (auto ns = sd->parent->isNspace()) {
          return strcmp(ns->toChars(), "std") == 0;
        }
      }
    }

    return false;
  }

public:
  AArch64TargetABI() : isDarwin(global.params.targetTriple->isOSDarwin()) {}

  bool returnInArg(TypeFunction *tf, bool) override {
    if (tf->isref()) {
      return false;
    }

    Type *rt = tf->next->toBasetype();
    if (rt->ty == TY::Tstruct || rt->ty == TY::Tsarray) {
      auto argTypes = getArgTypes(rt);
      return !argTypes // FIXME: get rid of sret workaround for 0-sized return
                       //        values (static arrays with 0 elements)
             || argTypes->arguments->empty();
    }

    return false;
  }

  // Prefer a ref if the POD cannot be passed in registers, i.e., if
  // IndirectByvalRewrite would be applied.
  bool preferPassByRef(Type *t) override {
    t = t->toBasetype();

    if (!(t->ty == TY::Tstruct || t->ty == TY::Tsarray))
      return false;

    auto argTypes = getArgTypes(t);
    return argTypes // not 0-sized
        && argTypes->arguments->empty(); // cannot be passed in registers
  }

  bool passByVal(TypeFunction *, Type *) override { return false; }

  void rewriteFunctionType(IrFuncTy &fty) override {
    if (!skipReturnValueRewrite(fty)) {
      rewriteArgument(fty, *fty.ret, /*isReturnVal=*/true);
    }

    for (auto arg : fty.args) {
      if (!arg->byref)
        rewriteArgument(fty, *arg, /*isReturnVal=*/false);
    }

    // remove 0-sized args (static arrays with 0 elements) and, for Darwin,
    // empty POD structs too
    size_t i = 0;
    while (i < fty.args.size()) {
      auto arg = fty.args[i];
      if (!arg->byref) {
        auto tb = arg->type->toBasetype();

        if (tb->size() == 0) {
          fty.args.erase(fty.args.begin() + i);
          continue;
        }

        // https://developer.apple.com/library/archive/documentation/Xcode/Conceptual/iPhoneOSABIReference/Articles/ARM64FunctionCallingConventions.html#//apple_ref/doc/uid/TP40013702-SW1
        if (isDarwin) {
          if (auto ts = tb->isTypeStruct()) {
            if (ts->sym->fields.empty() && ts->sym->isPOD()) {
              fty.args.erase(fty.args.begin() + i);
              continue;
            }
          }
        }
      }

      ++i;
    }
  }

  void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override {
    return rewriteArgument(fty, arg, /*isReturnVal=*/false);
  }

  void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg, bool isReturnVal) {
    Type *t = arg.type->toBasetype();

    if (!isAggregate(t))
      return;

    // compiler magic: pass va_list args implicitly by reference
    if (!isReturnVal && isAAPCS64VaList(t)) {
      arg.byref = true;
      arg.ltype = arg.ltype->getPointerTo();
      return;
    }

    auto argTypes = getArgTypes(t);
    if (!argTypes)
      return; // don't rewrite 0-sized types

    if (argTypes->arguments->empty()) {
      // non-PODs and larger non-HFVA aggregates are passed as pointer to
      // hidden copy
      indirectByvalRewrite.applyTo(arg);
      return;
    }

    // LLVM seems to take care of the rest when rewriting as follows, close to
    // what clang emits:

    auto rewrittenType = getRewrittenArgType(t, argTypes);
    if (!rewrittenType)
      return;

    if (rewrittenType->isIntegerTy()) {
      argTypesRewrite.applyToIfNotObsolete(arg, rewrittenType);
    } else {
      // in most cases, a LL array of either floats/vectors (HFVAs) or i64
      argTypesRewrite.applyTo(arg, rewrittenType);
    }
  }

  Type *vaListType() override {
    if (isDarwin)
      return TargetABI::vaListType(); // char*

    // We need to pass the actual va_list type for correct mangling. Simply
    // using TypeIdentifier here is a bit wonky but works, as long as the name
    // is actually available in the scope (this is what DMD does, so if a
    // better solution is found there, this should be adapted).
    return TypeIdentifier::create(Loc(), Identifier::idPool("__va_list"));
  }

  const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override {
    // see objc/message.h for objc_msgSend selection rules
    return "objc_msgSend";
  }
};

// The public getter for abi.cpp
TargetABI *getAArch64TargetABI() { return new AArch64TargetABI(); }