File: abi-x86-64.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 (402 lines) | stat: -rw-r--r-- 13,074 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
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
//===-- abi-x86-64.cpp ----------------------------------------------------===//
//
//                         LDC – the LLVM D compiler
//
// This file is distributed under the BSD-style LDC license. See the LICENSE
// file for details.
//
//===----------------------------------------------------------------------===//
//
// extern(C) implements the C calling convention for x86-64, as found in
// http://www.x86-64.org/documentation/abi-0.99.pdf
//
// Note:
//   Where a discrepancy was found between llvm-gcc and the ABI documentation,
//   llvm-gcc behavior was used for compatibility (after it was verified that
//   regular gcc has the same behavior).
//
// LLVM gets it right for most types, but complex numbers, structs and static
// arrays need some help. To make sure it gets those right we essentially
// bitcast these types to a type to which LLVM assigns the appropriate
// registers (using DMD's toArgTypes() machinery), and pass that instead.
// Structs that are required to be passed in memory are marked with the ByVal
// attribute to ensure no part of them ends up in registers when only a subset
// of the desired registers are available.
//
//===----------------------------------------------------------------------===//

#include "gen/abi-x86-64.h"

#include "dmd/aggregate.h"
#include "dmd/declaration.h"
#include "dmd/enum.h"
#include "dmd/id.h"
#include "dmd/identifier.h"
#include "dmd/mtype.h"
#include "dmd/target.h"
#include "gen/abi.h"
#include "gen/abi-generic.h"
#include "gen/abi-x86-64.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 <cassert>
#include <map>
#include <string>
#include <utility>

namespace {
struct RegCount {
  char int_regs, sse_regs;

  RegCount() : int_regs(6), sse_regs(8) {}

  explicit RegCount(LLType *ty) : int_regs(0), sse_regs(0) {
    if (LLStructType *structTy = isaStruct(ty)) {
      for (unsigned i = 0; i < structTy->getNumElements(); ++i) {
        RegCount elementRegCount(structTy->getElementType(i));
        int_regs += elementRegCount.int_regs;
        sse_regs += elementRegCount.sse_regs;
      }
    } else if (LLArrayType *arrayTy = isaArray(ty)) {
      char N = static_cast<char>(arrayTy->getNumElements());
      RegCount elementRegCount(arrayTy->getElementType());
      int_regs = N * elementRegCount.int_regs;
      sse_regs = N * elementRegCount.sse_regs;
    } else if (ty->isIntegerTy() || ty->isPointerTy()) {
      ++int_regs;
    } else if (ty->isFloatingPointTy() || ty->isVectorTy()) {
      // X87 reals are passed on the stack
      if (!ty->isX86_FP80Ty()) {
        ++sse_regs;
      }
    } else {
      unsigned sizeInBits = gDataLayout->getTypeSizeInBits(ty);
      IF_LOG Logger::cout() << "SysV RegCount: assuming 1 GP register for type "
                            << *ty << " (" << sizeInBits << " bits)\n";
      assert(sizeInBits > 0 && sizeInBits <= 64);
      ++int_regs;
    }

    assert(int_regs + sse_regs <= 2);
  }

  enum SubtractionResult {
    ArgumentFitsIn,
    ArgumentWouldFitInPartially,
    ArgumentDoesntFitIn
  };

  SubtractionResult trySubtract(const IrFuncTyArg &arg) {
    const RegCount wanted(arg.ltype);

    const bool anyRegAvailable = (wanted.int_regs > 0 && int_regs > 0) ||
                                 (wanted.sse_regs > 0 && sse_regs > 0);
    if (!anyRegAvailable) {
      return ArgumentDoesntFitIn;
    }

    if (int_regs < wanted.int_regs || sse_regs < wanted.sse_regs) {
      return ArgumentWouldFitInPartially;
    }

    int_regs -= wanted.int_regs;
    sse_regs -= wanted.sse_regs;

    return ArgumentFitsIn;
  }
};

/**
 * This type is used to force LLVM to pass a LL aggregate in memory,
 * on the function parameters stack. We need this to prevent LLVM
 * from passing an aggregate partially in registers, partially in
 * memory.
 * This is achieved by passing a pointer to the aggregate and using
 * the byval LLVM attribute.
 */
struct ImplicitByvalRewrite : ABIRewrite {
  LLValue *put(DValue *v, bool isLValueExp, bool isLastArgExp) override {
    if (isLValueExp && !isLastArgExp && v->isLVal()) {
      // copy to avoid visibility of potential side effects of later argument
      // expressions
      return DtoAllocaDump(v, ".lval_copy_for_ImplicitByvalRewrite");
    }
    return getAddressOf(v);
  }

  LLValue *getLVal(Type *dty, LLValue *v) override { return v; }

  LLType *type(Type *t) override { return DtoPtrToType(t); }

  void applyTo(IrFuncTyArg &arg, LLType *finalLType = nullptr) override {
    ABIRewrite::applyTo(arg, finalLType);
#if LDC_LLVM_VER >= 1200
    arg.attrs.addByValAttr(DtoType(arg.type));
#else
    arg.attrs.addAttribute(LLAttribute::ByVal);
#endif
    if (auto alignment = DtoAlignment(arg.type))
      arg.attrs.addAlignmentAttr(alignment);
  }
};
} // anonymous namespace

struct X86_64TargetABI : TargetABI {
  ArgTypesRewrite argTypesRewrite;
  ImplicitByvalRewrite byvalRewrite;
  IndirectByvalRewrite indirectByvalRewrite;

  bool returnInArg(TypeFunction *tf, bool needsThis) override;

  bool preferPassByRef(Type *t) override;

  bool passByVal(TypeFunction *tf, Type *t) override;

  void rewriteFunctionType(IrFuncTy &fty) override;
  void rewriteVarargs(IrFuncTy &fty, std::vector<IrFuncTyArg *> &args) override;
  void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override;
  void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg, RegCount &regCount);

  LLValue *prepareVaStart(DLValue *ap) override;

  void vaCopy(DLValue *dest, DValue *src) override;

  LLValue *prepareVaArg(DLValue *ap) override;

  Type *vaListType() override;

  const char *objcMsgSendFunc(Type *ret, IrFuncTy &fty) override;

private:
  LLType *getValistType();

  static bool passInMemory(Type* t) {
    TypeTuple *argTypes = getArgTypes(t);
    return argTypes && argTypes->arguments->empty();
  }

  // Helper treating the magic __c_complex_real enum as creal.
  static bool returnsComplexReal(TypeFunction* tf) {
    Type *rt = tf->next;
    if (auto te = rt->isTypeEnum()) {
      if (te->sym->ident == Id::__c_complex_real)
        return true;
    }
    return rt->toBasetype()->ty == TY::Tcomplex80;
  }

  RegCount &getRegCount(IrFuncTy &fty) {
    return reinterpret_cast<RegCount &>(fty.tag);
  }
};

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

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

  Type *rt = tf->next->toBasetype();

  // x87 creal is returned on the x87 stack
  if (returnsComplexReal(tf))
    return false;

  return passInMemory(rt);
}

// Prefer a ref if the POD cannot be passed in registers, i.e., if the LLVM
// ByVal attribute would be applied, *and* the size is > 16.
bool X86_64TargetABI::preferPassByRef(Type *t) {
  return t->size() > 16 && passInMemory(t->toBasetype());
}

bool X86_64TargetABI::passByVal(TypeFunction *tf, Type *t) {
  // indirectly by-value for non-POD args
  if (!isPOD(t))
    return false;

  return passInMemory(t->toBasetype());
}

void X86_64TargetABI::rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) {
  llvm_unreachable("Please use the other overload explicitly.");
}

void X86_64TargetABI::rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg,
                                      RegCount &regCount) {
  LLType *originalLType = arg.ltype;
  Type *t = arg.type->toBasetype();

  // indirectly by-value for non-POD args
  if (!isPOD(t)) {
    indirectByvalRewrite.applyTo(arg);
    if (regCount.int_regs > 0) {
      regCount.int_regs--;
    }

    return;
  }

  if (t->ty == TY::Tcomplex32 || t->ty == TY::Tstruct || t->ty == TY::Tsarray) {
    if (LLType *rewrittenType = getRewrittenArgType(t))
      argTypesRewrite.applyToIfNotObsolete(arg, rewrittenType);
  }

  if (regCount.trySubtract(arg) == RegCount::ArgumentWouldFitInPartially) {
    // pass the LL aggregate with byval attribute to prevent LLVM from passing
    // it partially in registers, partially in memory
    assert(originalLType->isAggregateType());
    IF_LOG Logger::cout() << "Passing byval to prevent register/memory mix: "
                          << arg.type->toChars() << " (" << *originalLType
                          << ")\n";
    byvalRewrite.applyTo(arg);
  }
}

void X86_64TargetABI::rewriteFunctionType(IrFuncTy &fty) {
  RegCount &regCount = getRegCount(fty);
  regCount = RegCount(); // initialize

  // RETURN VALUE
  if (!skipReturnValueRewrite(fty)) {
    Logger::println("x86-64 ABI: Transforming return type");
    LOG_SCOPE;

    // don't rewrite x87 creal return values (returned on the x87 stack)
    if (!returnsComplexReal(fty.type)) {
      RegCount dummy;
      rewriteArgument(fty, *fty.ret, dummy);
    }
  }

  // IMPLICIT PARAMETERS
  if (fty.arg_sret) {
    regCount.int_regs--;
  }
  if (fty.arg_this || fty.arg_nest) {
    regCount.int_regs--;
  }
  if (fty.arg_objcSelector) {
    regCount.int_regs--;
  }
  if (fty.arg_arguments) {
    regCount.int_regs -= 2; // dynamic array
  }

  // EXPLICIT PARAMETERS
  Logger::println("x86-64 ABI: Transforming argument types");
  LOG_SCOPE;

  for (IrFuncTyArg *arg : fty.args) {
    if (arg->byref) {
      if (!arg->isByVal() && regCount.int_regs > 0) {
        regCount.int_regs--;
      }
    } else {
      rewriteArgument(fty, *arg, regCount);
    }
  }

  // regCount (fty.tag) is now in the state after all implicit & formal args,
  // ready to serve as initial state for each vararg call site, see below
}

void X86_64TargetABI::rewriteVarargs(IrFuncTy &fty,
                                     std::vector<IrFuncTyArg *> &args) {
  // use a dedicated RegCount copy for each call site and initialize it with
  // fty.tag
  RegCount regCount = getRegCount(fty);

  for (auto arg : args) {
    if (!arg->byref) { // don't rewrite ByVal arguments
      rewriteArgument(fty, *arg, regCount);
    }
  }
}

/**
 * The System V AMD64 ABI uses a special native va_list type - a 24-bytes struct
 * passed by reference.
 * In druntime, the struct is aliased as object.__va_list; the actually used
 * core.stdc.stdarg.va_list type is a __va_list_tag* pointer though to achieve
 * byref semantics.
 * This requires a little bit of compiler magic in the following
 * implementations.
 */

LLType *X86_64TargetABI::getValistType() {
  LLType *uintType = LLType::getInt32Ty(gIR->context());
  LLType *voidPointerType = getVoidPtrType();

  std::vector<LLType *> parts;      // struct __va_list_tag {
  parts.push_back(uintType);        //   uint gp_offset;
  parts.push_back(uintType);        //   uint fp_offset;
  parts.push_back(voidPointerType); //   void* overflow_arg_area;
  parts.push_back(voidPointerType); //   void* reg_save_area; }

  return LLStructType::get(gIR->context(), parts);
}

LLValue *X86_64TargetABI::prepareVaStart(DLValue *ap) {
  // Since the user only created a __va_list_tag* pointer (ap) on the stack before
  // invoking va_start, we first need to allocate the actual __va_list_tag struct
  // and set `ap` to its address.
  LLValue *valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem");
  DtoStore(valistmem,
           DtoBitCast(DtoLVal(ap), getPtrToType(valistmem->getType())));
  // Pass a i8* pointer to the actual struct to LLVM's va_start intrinsic.
  return DtoBitCast(valistmem, getVoidPtrType());
}

void X86_64TargetABI::vaCopy(DLValue *dest, DValue *src) {
  // Analog to va_start, we first need to allocate a new __va_list_tag struct on
  // the stack and set `dest` to its address.
  LLValue *valistmem = DtoRawAlloca(getValistType(), 0, "__va_list_mem");
  DtoStore(valistmem,
           DtoBitCast(DtoLVal(dest), getPtrToType(valistmem->getType())));
  // Then fill the new struct with a bitcopy of the source struct.
  // `src` is a __va_list_tag* pointer to the source struct.
  DtoMemCpy(valistmem, DtoRVal(src));
}

LLValue *X86_64TargetABI::prepareVaArg(DLValue *ap) {
  // Pass a i8* pointer to the actual __va_list_tag struct to LLVM's va_arg
  // intrinsic.
  return DtoBitCast(DtoRVal(ap), getVoidPtrType());
}

Type *X86_64TargetABI::vaListType() {
  // 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"))
      ->pointerTo();
}

const char *X86_64TargetABI::objcMsgSendFunc(Type *ret,
                                             IrFuncTy &fty) {
  // see objc/message.h for objc_msgSend selection rules
  if (fty.arg_sret) {
    return "objc_msgSend_stret";
  }
  if (ret) {
    // complex long double return
    if (ret->ty == TY::Tcomplex80) {
      return "objc_msgSend_fp2ret";
    }
    // long double return
    if (ret->ty == TY::Tfloat80 || ret->ty == TY::Timaginary80) {
      return "objc_msgSend_fpret";
    }
  }
  return "objc_msgSend";
}