File: abi-x86.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 (293 lines) | stat: -rw-r--r-- 9,914 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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
//===-- abi-x86.cpp -------------------------------------------------------===//
//
//                         LDC – the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//

#include "dmd/enum.h"
#include "dmd/id.h"
#include "gen/abi-generic.h"
#include "gen/abi.h"
#include "gen/dvalue.h"
#include "gen/irstate.h"
#include "gen/llvm.h"
#include "gen/llvmhelpers.h"
#include "gen/logger.h"
#include "gen/tollvm.h"
#include "ir/irfunction.h"
#include "ir/irfuncty.h"

struct X86TargetABI : TargetABI {
  const bool isDarwin;
  const bool isMSVC;
  bool returnStructsInRegs;
  IntegerRewrite integerRewrite;
  IndirectByvalRewrite indirectByvalRewrite;

  X86TargetABI()
      : isDarwin(global.params.targetTriple->isOSDarwin()),
        isMSVC(global.params.targetTriple->isWindowsMSVCEnvironment()) {
    using llvm::Triple;
    auto os = global.params.targetTriple->getOS();
    returnStructsInRegs =
        !(os == Triple::Linux || os == Triple::Solaris || os == Triple::NetBSD);
  }

  llvm::CallingConv::ID callingConv(LINK l) override {
    switch (l) {
    case LINK::d:
    case LINK::default_:
    case LINK::windows:
      return llvm::CallingConv::X86_StdCall;
    default:
      return llvm::CallingConv::C;
    }
  }
  llvm::CallingConv::ID callingConv(TypeFunction *tf,
                                    bool withThisPtr) override {
    if (tf->parameterList.varargs == VARARGvariadic)
      return llvm::CallingConv::C;

    // MSVC++ passes the `this` pointer in ECX (D: EAX)
    // follow suit, incl. extern(C++) delegates
    if (isMSVC && tf->linkage == LINK::cpp && withThisPtr)
      return llvm::CallingConv::X86_ThisCall;

    return callingConv(tf->linkage);
  }

  std::string mangleFunctionForLLVM(std::string name, LINK l) override {
    if (global.params.targetTriple->isOSWindows()) {
      if (l == LINK::d || l == LINK::default_) {
        // Prepend a 0x1 byte to prevent LLVM from applying MS stdcall mangling:
        // _D… => __D…@<paramssize>, and add extra underscore manually.
        name.insert(0, "\1_");
      } else if (l == LINK::cpp && name[0] == '?') {
        // Prepend a 0x1 byte to prevent LLVM from prepending the C underscore
        // for MSVC++ symbols (starting with '?').
        name.insert(0, "\1");
      }
    }
    return name;
  }

  std::string mangleVariableForLLVM(std::string name, LINK l) override {
    if (global.params.targetTriple->isOSWindows() && l == LINK::cpp &&
        name[0] == '?') {
      // Prepend a 0x1 byte to prevent LLVM from prepending the C underscore for
      // MSVC++ symbols (starting with '?').
      name.insert(0, "\1");
    }
    return name;
  }

  // Helper folding the magic __c_complex_{float,double,real} enums to the basic
  // complex type.
  static Type *getExtraLoweredReturnType(TypeFunction *tf) {
    Type *rt = tf->next;
    if (auto te = rt->isTypeEnum()) {
      auto id = te->sym->ident;
      if (id == Id::__c_complex_float)
        return Type::tcomplex32;
      if (id == Id::__c_complex_double)
        return Type::tcomplex64;
      if (id == Id::__c_complex_real)
        return Type::tcomplex80;
    }
    return rt->toBasetype();
  }

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

    Type *rt = getExtraLoweredReturnType(tf);
    const bool externD = isExternD(tf);

    // non-aggregates are returned directly
    if (!isAggregate(rt))
      return false;

    // complex numbers
    if (rt->iscomplex()) {
      // extern(D): let LLVM return them directly as LL aggregates
      if (externD)
        return false;
      // extern(C) and all others:
      // * cfloat will be rewritten as 64-bit integer and returned in registers
      // * sret for cdouble and creal
      return rt->ty != TY::Tcomplex32;
    }

    // non-extern(D): some OSs don't return structs in registers at all
    if (!externD && !returnStructsInRegs)
      return true;

    const bool isMSVCpp = isMSVC && tf->linkage == LINK::cpp;

    // for non-static member functions, MSVC++ enforces sret for all structs
    if (isMSVCpp && needsThis && rt->ty == TY::Tstruct) {
      return true;
    }

    // force sret for non-POD structs
    const bool excludeStructsWithCtor = isMSVCpp;
    if (!isPOD(rt, excludeStructsWithCtor))
      return true;

    // return aggregates of a power-of-2 size <= 8 bytes in register(s),
    // all others via sret
    return !canRewriteAsInt(rt);
  }

  bool passByVal(TypeFunction *tf, Type *t) override {
    // indirectly by-value for non-POD args (except for MSVC++)
    const bool isMSVCpp = isMSVC && tf->linkage == LINK::cpp;
    if (!isMSVCpp && !isPOD(t))
      return false;

    // pass all structs and static arrays with the LLVM byval attribute
    return DtoIsInMemoryOnly(t);
  }

  void rewriteFunctionType(IrFuncTy &fty) override {
    const bool externD = isExternD(fty.type);

    // return value:
    if (!skipReturnValueRewrite(fty)) {
      Type *rt = getExtraLoweredReturnType(fty.type);
      if (isAggregate(rt) && canRewriteAsInt(rt) &&
          // don't rewrite cfloat for extern(D)
          !(externD && rt->ty == TY::Tcomplex32)) {
        integerRewrite.applyToIfNotObsolete(*fty.ret);
      }
    }

    // non-POD args are passed indirectly by-value (except for MSVC++)
    const bool isMSVCpp = isMSVC && fty.type->linkage == LINK::cpp;
    if (!isMSVCpp) {
      for (auto arg : fty.args) {
        if (!arg->byref && !isPOD(arg->type))
          indirectByvalRewrite.applyTo(*arg);
      }
    }

    // extern(D): try passing an argument in EAX
    if (externD) {

      // try an implicit argument...
      if (fty.arg_this) {
        Logger::println("Putting 'this' in register");
        fty.arg_this->attrs.addAttribute(LLAttribute::InReg);
      } else if (fty.arg_nest) {
        Logger::println("Putting context ptr in register");
        fty.arg_nest->attrs.addAttribute(LLAttribute::InReg);
      } else if (IrFuncTyArg *sret = fty.arg_sret) {
        Logger::println("Putting sret ptr in register");
        // sret and inreg are incompatible, but the ABI requires the
        // sret parameter to be in EAX in this situation...
        sret->attrs.removeAttribute(LLAttribute::StructRet);
        sret->attrs.addAttribute(LLAttribute::InReg);
      }

      // ... otherwise try the first argument
      else if (!fty.args.empty()) {
        // The first parameter is passed in EAX rather than being pushed on the
        // stack if the following conditions are met:
        //   * It fits in EAX.
        //   * It is not a 3 byte struct.
        //   * It is not a floating point type.

        IrFuncTyArg &first = *fty.args[0];
        if (first.rewrite == &indirectByvalRewrite ||
            (first.byref && !first.isByVal())) {
          Logger::println("Putting first (byref) parameter in register");
          first.attrs.addAttribute(LLAttribute::InReg);
        } else {
          Type *firstTy = first.type->toBasetype();
          auto sz = firstTy->size();
          if (!firstTy->isfloating() && (sz == 1 || sz == 2 || sz == 4)) {
            // rewrite aggregates as integers to make inreg work
            if (firstTy->ty == TY::Tstruct || firstTy->ty == TY::Tsarray) {
              integerRewrite.applyTo(first);
              // undo byval semantics applied via passByVal() returning true
              first.byref = false;
              first.attrs.clear();
            }
            first.attrs.addAttribute(LLAttribute::InReg);
          }
        }
      }
    }

    workaroundIssue1356(fty.args);

    // Clang does not pass empty structs, while it seems that GCC does,
    // at least on Linux x86. We don't know whether the C compiler will
    // be Clang or GCC, so just assume Clang on Darwin and G++ on Linux.
    if (externD || !isDarwin)
      return;

    size_t i = 0;
    while (i < fty.args.size()) {
      Type *type = fty.args[i]->type->toBasetype();
      if (type->ty == TY::Tstruct) {
        // Do not pass empty structs at all for C++ ABI compatibility.
        // Tests with clang reveal that more complex "empty" types, for
        // example a struct containing an empty struct, are not
        // optimized in the same way.
        auto sd = static_cast<TypeStruct *>(type)->sym;
        if (sd->fields.empty()) {
          fty.args.erase(fty.args.begin() + i);
          continue;
        }
      }
      ++i;
    }
  }

  void rewriteVarargs(IrFuncTy &fty,
                      std::vector<IrFuncTyArg *> &args) override {
    TargetABI::rewriteVarargs(fty, args);
    workaroundIssue1356(args);
  }

  // FIXME: LDC issue #1356
  // MSVC targets don't support alignment attributes for LL byval args
  void workaroundIssue1356(std::vector<IrFuncTyArg *> &args) const {
    if (isMSVC) {
      for (auto arg : args) {
        if (arg->isByVal()) {
#if LDC_LLVM_VER < 1300
          arg->attrs.removeAttribute(LLAttribute::Alignment);
#else
          // Keep alignment for LLVM 13+, to prevent invalid `movaps` etc.,
          // but limit to 4 (required according to runnable/ldc_cabi1.d).
          auto align4 = LLAlign(4);
          if (arg->attrs.getAlignment().getValueOr(align4) > align4)
            arg->attrs.addAlignmentAttr(align4);
#endif
        }
      }
    }
  }

  const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override {
    // see objc/message.h for objc_msgSend selection rules
    assert(isDarwin);
    if (fty.arg_sret) {
      return "objc_msgSend_stret";
    }
    // float, double, long double return
    if (ret && ret->isfloating() && !ret->iscomplex()) {
      return "objc_msgSend_fpret";
    }
    return "objc_msgSend";
  }
};

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