void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) override { const bool externD = (tf->linkage == LINKd && tf->varargs != 1); // return value: if (!fty.ret->byref) { Type *rt = tf->next->toBasetype(); // for sret, rt == void if (isAggregate(rt) && !isMagicCppStruct(rt) && canRewriteAsInt(rt) && // don't rewrite cfloat for extern(D) !(externD && rt->ty == Tcomplex32) && !integerRewrite.isObsoleteFor(fty.ret->ltype)) { fty.ret->rewrite = &integerRewrite; fty.ret->ltype = integerRewrite.type(fty.ret->type, fty.ret->ltype); } } // 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.add(LLAttribute::InReg); } else if (fty.arg_nest) { Logger::println("Putting context ptr in register"); fty.arg_nest->attrs.add(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.remove(LLAttribute::StructRet).add(LLAttribute::InReg); } // ... otherwise try the last argument else if (!fty.args.empty()) { // The last 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 *last = fty.args.back(); Type *lastTy = last->type->toBasetype(); unsigned sz = lastTy->size(); if (last->byref && !last->isByVal()) { Logger::println("Putting last (byref) parameter in register"); last->attrs.add(LLAttribute::InReg); } else if (!lastTy->isfloating() && (sz == 1 || sz == 2 || sz == 4)) { // rewrite aggregates as integers to make inreg work if (lastTy->ty == Tstruct || lastTy->ty == Tsarray) { last->rewrite = &integerRewrite; last->ltype = integerRewrite.type(last->type, last->ltype); // undo byval semantics applied via passByVal() returning true last->byref = false; last->attrs.clear(); } last->attrs.add(LLAttribute::InReg); } } // all other arguments are passed on the stack, don't rewrite // reverse parameter order if (fty.args.size() > 1) { fty.reverseParams = true; } } // FIXME: byval params with alignment attributes lead to crashes with MSVC if (isMSVC) { for (auto arg : fty.args) { if (arg->isByVal()) { arg->attrs.remove(LLAttribute::Alignment); } } } // 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 OS X and G++ on Linux. if (externD || !isOSX) return; size_t i = 0; while (i < fty.args.size()) { Type *type = fty.args[i]->type->toBasetype(); if (type->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; } }