void rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg) { Type* ty = arg.type->toBasetype(); if (ty->ty == Tstruct || ty->ty == Tsarray) { if (canRewriteAsInt(ty)) { arg.rewrite = &integerRewrite; arg.ltype = integerRewrite.type(arg.type, arg.ltype); } else { // these types are passed byval: // the caller allocates a copy and then passes a pointer to the copy arg.rewrite = &byvalRewrite; arg.ltype = byvalRewrite.type(arg.type, arg.ltype); // the copy is treated as a local variable of the callee // hence add the NoAlias and NoCapture attributes arg.attrs.clear() .add(LDC_ATTRIBUTE(NoAlias)) .add(LDC_ATTRIBUTE(NoCapture)); } } }
void Win64TargetABI::rewriteArgument(IrFuncTy& fty, IrFuncTyArg& arg) { LLType* originalLType = arg.ltype; Type* t = arg.type->toBasetype(); if (isPassedWithByvalSemantics(t)) { // these types are passed byval: // the caller allocates a copy and then passes a pointer to the copy arg.rewrite = &byvalRewrite; arg.ltype = byvalRewrite.type(arg.type, arg.ltype); // the copy is treated as a local variable of the callee // hence add the NoAlias and NoCapture attributes arg.attrs.clear() .add(LDC_ATTRIBUTE(NoAlias)) .add(LDC_ATTRIBUTE(NoCapture)); } else if (isAggregate(t) && canRewriteAsInt(t) && !IntegerRewrite::isObsoleteFor(originalLType)) { arg.rewrite = &integerRewrite; arg.ltype = integerRewrite.type(arg.type, arg.ltype); } IF_LOG if (arg.rewrite) { Logger::println("Rewriting argument type %s", t->toChars()); LOG_SCOPE; Logger::cout() << *originalLType << " => " << *arg.ltype << '\n'; } }
void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) override { Type *retTy = fty.ret->type->toBasetype(); if (!fty.ret->byref && retTy->ty == Tstruct) { // Rewrite HFAs only because union HFAs are turned into IR types that are // non-HFA and messes up register selection if (isHFA((TypeStruct *)retTy, &fty.ret->ltype)) { fty.ret->rewrite = &hfaToArray; fty.ret->ltype = hfaToArray.type(fty.ret->type); } else { fty.ret->rewrite = &integerRewrite; fty.ret->ltype = integerRewrite.type(fty.ret->type); } } for (auto arg : fty.args) { if (!arg->byref) rewriteArgument(fty, *arg); } // extern(D): reverse parameter order for non variadics, for DMD-compliance if (tf->linkage == LINKd && tf->varargs != 1 && fty.args.size() > 1) { fty.reverseParams = true; } }
void rewriteFunctionType(IrFuncTy &fty) override { Type *retTy = fty.ret->type->toBasetype(); if (!fty.ret->byref && retTy->ty == Tstruct) { // Rewrite HFAs only because union HFAs are turned into IR types that are // non-HFA and messes up register selection if (isHFA((TypeStruct *)retTy, &fty.ret->ltype)) { hfaToArray.applyTo(*fty.ret, fty.ret->ltype); } else { integerRewrite.applyTo(*fty.ret); } } for (auto arg : fty.args) { if (!arg->byref) rewriteArgument(fty, *arg); } }
void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override { Type *ty = arg.type->toBasetype(); if (ty->ty == Tstruct || ty->ty == Tsarray) { if (ty->ty == Tstruct && isHFA((TypeStruct *)ty, &arg.ltype, 8)) { arg.rewrite = &hfaToArray; arg.ltype = hfaToArray.type(arg.type); } else if (canRewriteAsInt(ty, true)) { arg.rewrite = &integerRewrite; arg.ltype = integerRewrite.type(arg.type); } else { arg.rewrite = &compositeToArray64; arg.ltype = compositeToArray64.type(arg.type); } } else if (ty->isintegral()) { arg.attrs.add(ty->isunsigned() ? LLAttribute::ZExt : LLAttribute::SExt); } }
void rewriteFunctionType(TypeFunction *tf, IrFuncTy &fty) override { Type *retTy = fty.ret->type->toBasetype(); if (!fty.ret->byref && retTy->ty == Tstruct) { // Rewrite HFAs only because union HFAs are turned into IR types that are // non-HFA and messes up register selection if (isHFA((TypeStruct *)retTy, &fty.ret->ltype)) { fty.ret->rewrite = &hfaToArray; fty.ret->ltype = hfaToArray.type(fty.ret->type); } else { fty.ret->rewrite = &integerRewrite; fty.ret->ltype = integerRewrite.type(fty.ret->type); } } for (auto arg : fty.args) { if (!arg->byref) rewriteArgument(fty, *arg); else if (passByVal(arg->type)) arg->attrs.remove(LLAttribute::ByVal); } }
void rewriteArgument(IrFuncTy &fty, IrFuncTyArg &arg) override { Type *ty = arg.type->toBasetype(); if (ty->ty == Tstruct || ty->ty == Tsarray) { if (canRewriteAsInt(ty, Is64Bit)) { if (!IntegerRewrite::isObsoleteFor(arg.ltype)) { arg.rewrite = &integerRewrite; arg.ltype = integerRewrite.type(arg.type, arg.ltype); } } else { // these types are passed byval: // the caller allocates a copy and then passes a pointer to the copy arg.rewrite = &byvalRewrite; arg.ltype = byvalRewrite.type(arg.type, arg.ltype); // the copy is treated as a local variable of the callee // hence add the NoAlias and NoCapture attributes arg.attrs.clear() .add(LLAttribute::NoAlias) .add(LLAttribute::NoCapture) .addAlignment(byvalRewrite.alignment(arg.type)); } } }
void rewriteFunctionType(IrFuncTy &fty) override { const bool externD = (fty.type->linkage == LINKd && fty.type->parameterList.varargs != VARARGvariadic); // return value: if (!fty.ret->byref) { Type *rt = fty.type->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.applyToIfNotObsolete(*fty.ret); } } // 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 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.addAttribute(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) { integerRewrite.applyTo(*last); // undo byval semantics applied via passByVal() returning true last->byref = false; last->attrs.clear(); } last->attrs.addAttribute(LLAttribute::InReg); } } // all other arguments are passed on the stack, don't rewrite } // extern(C++) on Posix: non-POD args are passed indirectly by-value else if (!isMSVC && fty.type->linkage == LINKcpp) { for (auto arg : fty.args) { if (!arg->byref && !isPOD(arg->type)) indirectByvalRewrite.applyTo(*arg); } } 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 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; } }
void rewriteFunctionType(TypeFunction* tf, IrFuncTy &fty) { // extern(D) if (tf->linkage == LINKd) { // IMPLICIT PARAMETERS // mark this/nested params inreg if (fty.arg_this) { Logger::println("Putting 'this' in register"); fty.arg_this->attrs.clear() .add(LDC_ATTRIBUTE(InReg)); } else if (fty.arg_nest) { Logger::println("Putting context ptr in register"); fty.arg_nest->attrs.clear() .add(LDC_ATTRIBUTE(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.add(LDC_ATTRIBUTE(InReg)).remove(LDC_ATTRIBUTE(StructRet)); } // otherwise try to mark the last param inreg 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(LDC_ATTRIBUTE(InReg)); } else if (!lastTy->isfloating() && (sz == 1 || sz == 2 || sz == 4)) // right? { // rewrite the struct into an integer to make inreg work if (lastTy->ty == Tstruct || lastTy->ty == Tsarray) { last->rewrite = &integerRewrite; last->ltype = integerRewrite.type(last->type, last->ltype); last->byref = false; // erase previous attributes last->attrs.clear(); } last->attrs.add(LDC_ATTRIBUTE(InReg)); } } // FIXME: tf->varargs == 1 need to use C calling convention and vararg mechanism to live up to the spec: // "The caller is expected to clean the stack. _argptr is not passed, it is computed by the callee." // EXPLICIT PARAMETERS // reverse parameter order // for non variadics if (!fty.args.empty() && tf->varargs != 1) { fty.reverseParams = true; } } // extern(C) and all others else { // RETURN VALUE // cfloat -> i64 if (tf->next->toBasetype() == Type::tcomplex32) { fty.ret->rewrite = &integerRewrite; fty.ret->ltype = integerRewrite.type(fty.ret->type, fty.ret->ltype); } // IMPLICIT PARAMETERS // EXPLICIT PARAMETERS } }