Example #1
0
  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;
    }
  }