#include "gen/uda.h" #include "gen/llvm.h" #include "gen/llvmhelpers.h" #include "aggregate.h" #include "attrib.h" #include "declaration.h" #include "expression.h" #include "ir/irfunction.h" #include "module.h" #include "llvm/ADT/StringExtras.h" namespace { /// Names of the attribute structs we recognize. namespace attr { const std::string allocSize = "allocSize"; const std::string llvmAttr = "llvmAttr"; const std::string llvmFastMathFlag = "llvmFastMathFlag"; const std::string optStrategy = "optStrategy"; const std::string section = "section"; const std::string target = "target"; const std::string weak = "_weak"; } /// Checks whether `moduleDecl` is the ldc.attributes module. bool isLdcAttibutes(const ModuleDeclaration *moduleDecl) { if (!moduleDecl) return false; if (strcmp("attributes", moduleDecl->id->string)) { return false; } if (moduleDecl->packages->dim != 1 || strcmp("ldc", (*moduleDecl->packages)[0]->string)) { return false; } return true; } /// Checks whether the type of `e` is a struct from the ldc.attributes module. bool isFromLdcAttibutes(const StructLiteralExp *e) { auto moduleDecl = e->sd->getModule()->md; return isLdcAttibutes(moduleDecl); } StructLiteralExp *getLdcAttributesStruct(Expression *attr) { // See whether we can evaluate the attribute at compile-time. All the LDC // attributes are struct literals that may be constructed using a CTFE // function. unsigned prevErrors = global.startGagging(); auto e = ctfeInterpret(attr); if (global.endGagging(prevErrors)) { return nullptr; } if (e->op != TOKstructliteral) { return nullptr; } auto sle = static_cast(e); if (isFromLdcAttibutes(sle)) { return sle; } return nullptr; } void checkStructElems(StructLiteralExp *sle, ArrayParam elemTypes) { if (sle->elements->dim != elemTypes.size()) { sle->error( "unexpected field count in 'ldc.attributes.%s'; does druntime not " "match compiler version?", sle->sd->ident->string); fatal(); } for (size_t i = 0; i < sle->elements->dim; ++i) { if ((*sle->elements)[i]->type != elemTypes[i]) { sle->error("invalid field type in 'ldc.attributes.%s'; does druntime not " "match compiler version?", sle->sd->ident->string); fatal(); } } } /// Returns the StructLiteralExp magic attribute with name `name` if it is /// applied to `sym`, otherwise returns nullptr. StructLiteralExp *getMagicAttribute(Dsymbol *sym, std::string name) { if (!sym->userAttribDecl) return nullptr; // Loop over all UDAs and early return the expression if a match was found. Expressions *attrs = sym->userAttribDecl->getAttributes(); expandTuples(attrs); for (auto &attr : *attrs) { auto sle = getLdcAttributesStruct(attr); if (!sle) continue; if (name == sle->sd->ident->string) { return sle; } } return nullptr; } sinteger_t getIntElem(StructLiteralExp *sle, size_t idx) { auto arg = (*sle->elements)[idx]; return arg->toInteger(); } /// Returns a null-terminated string const char *getStringElem(StructLiteralExp *sle, size_t idx) { auto arg = (*sle->elements)[idx]; if (arg && arg->op == TOKstring) { auto strexp = static_cast(arg); assert(strexp->sz == 1); return strexp->toStringz(); } // Default initialized element (arg->op == TOKnull) return ""; } /// Returns a null-terminated string const char *getFirstElemString(StructLiteralExp *sle) { return getStringElem(sle, 0); } // @allocSize(1) // @allocSize(0,2) void applyAttrAllocSize(StructLiteralExp *sle, IrFunction *irFunc) { llvm::Function *func = irFunc->getLLVMFunc(); checkStructElems(sle, {Type::tint32, Type::tint32}); auto sizeArgIdx = getIntElem(sle, 0); auto numArgIdx = getIntElem(sle, 1); // Get the number of parameters that the user specified (excluding the // implicit `this` parameter) auto numUserParams = irFunc->irFty.args.size(); // Get the number of parameters of the function in LLVM IR. This includes // the `this` and sret parameters. auto llvmNumParams = irFunc->irFty.funcType->getNumParams(); // Verify that the index values are valid bool error = false; if (sizeArgIdx + 1 > sinteger_t(numUserParams)) { sle->error("@ldc.attributes.allocSize.sizeArgIdx=%d too large for function " "`%s` with %d arguments.", (int)sizeArgIdx, irFunc->decl->toChars(), (int)numUserParams); error = true; } if (numArgIdx + 1 > sinteger_t(numUserParams)) { sle->error("@ldc.attributes.allocSize.numArgIdx=%d too large for function " "`%s` with %d arguments.", (int)numArgIdx, irFunc->decl->toChars(), (int)numUserParams); error = true; } if (error) return; // The allocSize attribute is only effective for LLVM >= 3.9. #if LDC_LLVM_VER >= 309 // Offset to correct indices for sret and this parameters. // These parameters can never be used for allocsize, and the user-specified // index does not account for these. unsigned offset = llvmNumParams - numUserParams; // Calculate the param indices for the function as defined in LLVM IR auto llvmSizeIdx = irFunc->irFty.reverseParams ? numUserParams - sizeArgIdx - 1 : sizeArgIdx; auto llvmNumIdx = irFunc->irFty.reverseParams ? numUserParams - numArgIdx - 1 : numArgIdx; llvmSizeIdx += offset; llvmNumIdx += offset; llvm::AttrBuilder builder; if (numArgIdx >= 0) { builder.addAllocSizeAttr(llvmSizeIdx, llvmNumIdx); } else { builder.addAllocSizeAttr(llvmSizeIdx, llvm::Optional()); } func->addAttributes(llvm::AttributeSet::FunctionIndex, llvm::AttributeSet::get(func->getContext(), llvm::AttributeSet::FunctionIndex, builder)); #endif } // @llvmAttr("key", "value") // @llvmAttr("key") void applyAttrLLVMAttr(StructLiteralExp *sle, llvm::Function *func) { checkStructElems(sle, {Type::tstring, Type::tstring}); llvm::StringRef key = getStringElem(sle, 0); llvm::StringRef value = getStringElem(sle, 1); if (value.empty()) { func->addFnAttr(key); } else { func->addFnAttr(key, value); } } // @llvmFastMathFlag("flag") void applyAttrLLVMFastMathFlag(StructLiteralExp *sle, IrFunction *irFunc) { checkStructElems(sle, {Type::tstring}); llvm::StringRef value = getStringElem(sle, 0); if (value == "clear") { irFunc->FMF.clear(); } else if (value == "fast") { irFunc->FMF.setUnsafeAlgebra(); } else if (value == "nnan") { irFunc->FMF.setNoNaNs(); } else if (value == "ninf") { irFunc->FMF.setNoInfs(); } else if (value == "nsz") { irFunc->FMF.setNoSignedZeros(); } else if (value == "arcp") { irFunc->FMF.setAllowReciprocal(); } else { // `value` is a null-terminated returned from getStringElem so can be passed // to warning("... %s ..."). sle->warning( "ignoring unrecognized flag parameter '%s' for '@ldc.attributes.%s'", value.data(), sle->sd->ident->string); } } void applyAttrOptStrategy(StructLiteralExp *sle, IrFunction *irFunc) { checkStructElems(sle, {Type::tstring}); llvm::StringRef value = getStringElem(sle, 0); llvm::Function *func = irFunc->getLLVMFunc(); if (value == "none") { if (irFunc->decl->inlining == PINLINEalways) { sle->error("cannot combine '@ldc.attributes.%s(\"none\")' with " "'pragma(inline, true)'", sle->sd->ident->string); return; } irFunc->decl->inlining = PINLINEnever; func->addFnAttr(llvm::Attribute::OptimizeNone); } else if (value == "optsize") { func->addFnAttr(llvm::Attribute::OptimizeForSize); } else if (value == "minsize") { func->addFnAttr(llvm::Attribute::MinSize); } else { sle->warning( "ignoring unrecognized parameter '%s' for '@ldc.attributes.%s'", value.data(), sle->sd->ident->string); } } void applyAttrSection(StructLiteralExp *sle, llvm::GlobalObject *globj) { checkStructElems(sle, {Type::tstring}); globj->setSection(getFirstElemString(sle)); } void applyAttrTarget(StructLiteralExp *sle, llvm::Function *func) { // TODO: this is a rudimentary implementation for @target. Many more // target-related attributes could be applied to functions (not just for // @target): clang applies many attributes that LDC does not. // The current implementation here does not do any checking of the specified // string and simply passes all to llvm. checkStructElems(sle, {Type::tstring}); std::string targetspec = getFirstElemString(sle); if (targetspec.empty() || targetspec == "default") return; llvm::StringRef CPU; std::vector features; if (func->hasFnAttribute("target-features")) { auto attr = func->getFnAttribute("target-features"); features.push_back(attr.getValueAsString()); } llvm::SmallVector fragments; llvm::SplitString(targetspec, fragments, ","); // special strings: "arch=", "tune=<...>", "fpmath=<...>" // if string starts with "no-", strip "no" // otherwise add "+" for (auto s : fragments) { s = s.trim(); if (s.empty()) continue; if (s.startswith("arch=")) { // TODO: be smarter than overwriting the previous arch= setting CPU = s.drop_front(5); continue; } if (s.startswith("tune=")) { // clang 3.8 ignores tune= too continue; } if (s.startswith("fpmath=")) { // TODO: implementation; clang 3.8 ignores fpmath= too continue; } if (s.startswith("no-")) { std::string f = (std::string("-") + s.drop_front(3)).str(); features.emplace_back(std::move(f)); continue; } std::string f = (std::string("+") + s).str(); features.emplace_back(std::move(f)); } if (!CPU.empty()) func->addFnAttr("target-cpu", CPU); if (!features.empty()) { // Sorting the features puts negative features ("-") after positive features // ("+"). This provides the desired behavior of negative features overriding // positive features regardless of their order in the source code. sort(features.begin(), features.end()); func->addFnAttr("target-features", llvm::join(features.begin(), features.end(), ",")); } } } // anonymous namespace void applyVarDeclUDAs(VarDeclaration *decl, llvm::GlobalVariable *gvar) { if (!decl->userAttribDecl) return; Expressions *attrs = decl->userAttribDecl->getAttributes(); expandTuples(attrs); for (auto &attr : *attrs) { auto sle = getLdcAttributesStruct(attr); if (!sle) continue; auto name = sle->sd->ident->string; if (name == attr::section) { applyAttrSection(sle, gvar); } else if (name == attr::optStrategy || name == attr::target) { sle->error( "Special attribute 'ldc.attributes.%s' is only valid for functions", name); } else if (name == attr::weak) { // @weak is applied elsewhere } else { sle->warning( "Ignoring unrecognized special attribute 'ldc.attributes.%s'", name); } } } void applyFuncDeclUDAs(FuncDeclaration *decl, IrFunction *irFunc) { if (!decl->userAttribDecl) return; llvm::Function *func = irFunc->getLLVMFunc(); assert(func); Expressions *attrs = decl->userAttribDecl->getAttributes(); expandTuples(attrs); for (auto &attr : *attrs) { auto sle = getLdcAttributesStruct(attr); if (!sle) continue; auto name = sle->sd->ident->string; if (name == attr::allocSize) { applyAttrAllocSize(sle, irFunc); } else if (name == attr::llvmAttr) { applyAttrLLVMAttr(sle, func); } else if (name == attr::llvmFastMathFlag) { applyAttrLLVMFastMathFlag(sle, irFunc); } else if (name == attr::optStrategy) { applyAttrOptStrategy(sle, irFunc); } else if (name == attr::section) { applyAttrSection(sle, func); } else if (name == attr::target) { applyAttrTarget(sle, func); } else if (name == attr::weak) { // @weak is applied elsewhere } else { sle->warning( "Ignoring unrecognized special attribute 'ldc.attributes.%s'", name); } } } /// Checks whether 'sym' has the @ldc.attributes._weak() UDA applied. bool hasWeakUDA(Dsymbol *sym) { auto sle = getMagicAttribute(sym, attr::weak); if (!sle) return false; checkStructElems(sle, {}); auto vd = sym->isVarDeclaration(); if (!(vd && vd->isDataseg()) && !sym->isFuncDeclaration()) sym->error("@ldc.attributes.weak can only be applied to functions or " "global variables"); return true; }