mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-04-27 13:40:33 +03:00

Using `mangleOverride` directly instead. Also get rid of superfluous string copies. And for LLVMatomic_rmw, don't store the operation string in the instantiated FuncDeclarations anymore, but fetch it from the parent TemplateDeclaration.
609 lines
17 KiB
C++
609 lines
17 KiB
C++
//===-- pragma.cpp --------------------------------------------------------===//
|
||
//
|
||
// LDC – the LLVM D compiler
|
||
//
|
||
// This file is distributed under the BSD-style LDC license. See the LICENSE
|
||
// file for details.
|
||
//
|
||
//===----------------------------------------------------------------------===//
|
||
|
||
#include "gen/pragma.h"
|
||
|
||
#include "dmd/attrib.h"
|
||
#include "dmd/declaration.h"
|
||
#include "dmd/errors.h"
|
||
#include "dmd/expression.h"
|
||
#include "dmd/id.h"
|
||
#include "dmd/identifier.h"
|
||
#include "dmd/module.h"
|
||
#include "dmd/scope.h"
|
||
#include "dmd/template.h"
|
||
#include "gen/inlineir.h"
|
||
#include "gen/llvmhelpers.h"
|
||
#include "llvm/Support/CommandLine.h"
|
||
|
||
using namespace dmd;
|
||
|
||
namespace {
|
||
bool parseStringExp(Expression *e, const char *&res) {
|
||
e = optimize(e, WANTvalue);
|
||
if (e->op != EXP::string_) {
|
||
return false;
|
||
}
|
||
auto se = static_cast<StringExp *>(e);
|
||
auto size = (se->len + 1) * se->sz;
|
||
auto s = static_cast<char *>(mem.xmalloc(size));
|
||
se->writeTo(s, true);
|
||
res = s;
|
||
return true;
|
||
}
|
||
|
||
bool parseIntExp(Expression *e, dinteger_t &res) {
|
||
e = optimize(e, WANTvalue);
|
||
if (auto i = e->isIntegerExp()) {
|
||
res = i->getInteger();
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
bool parseBoolExp(Expression *e, bool &res) {
|
||
e = optimize(e, WANTvalue);
|
||
if (auto i = e->isIntegerExp()) {
|
||
if (e->type->equals(Type::tbool)) {
|
||
res = (i->toInteger() != 0);
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
}
|
||
|
||
// Applies an action to matching symbols, recursively descending into nested
|
||
// AttribDeclarations, and returns the number of applications.
|
||
template <typename T>
|
||
int applyPragma(Dsymbol *s, std::function<T *(Dsymbol *)> predicate,
|
||
std::function<void(T *)> action) {
|
||
if (T *matchingDecl = predicate(s)) {
|
||
if (matchingDecl->llvmInternal != LLVMnone) {
|
||
error(s->loc, "multiple LDC specific pragmas are not allowed");
|
||
fatal();
|
||
}
|
||
action(matchingDecl);
|
||
return 1;
|
||
}
|
||
|
||
if (auto ad = s->isAttribDeclaration()) {
|
||
if (ad->decl) {
|
||
int count = 0;
|
||
for (auto child : *ad->decl) {
|
||
count += applyPragma(child, predicate, action);
|
||
}
|
||
return count;
|
||
}
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
|
||
int applyFunctionPragma(Dsymbol *s,
|
||
std::function<void(FuncDeclaration *)> action) {
|
||
return applyPragma<FuncDeclaration>(
|
||
s, [](Dsymbol *s) { return s->isFuncDeclaration(); }, action);
|
||
}
|
||
|
||
int applyTemplatePragma(Dsymbol *s,
|
||
std::function<void(TemplateDeclaration *)> action) {
|
||
return applyPragma<TemplateDeclaration>(
|
||
s, [](Dsymbol *s) { return s->isTemplateDeclaration(); }, action);
|
||
}
|
||
|
||
int applyVariablePragma(Dsymbol *s,
|
||
std::function<void(VarDeclaration *)> action) {
|
||
return applyPragma<VarDeclaration>(
|
||
s, [](Dsymbol *s) { return s->isVarDeclaration(); }, action);
|
||
}
|
||
} // anonymous namespace
|
||
|
||
LDCPragma DtoGetPragma(Scope *sc, PragmaDeclaration *decl,
|
||
const char *&arg1str) {
|
||
Identifier *ident = decl->ident;
|
||
Expressions *args = decl->args;
|
||
|
||
const auto getFirstArg = [args, sc]() {
|
||
return (args && args->length > 0) ? expressionSemantic((*args)[0], sc)
|
||
: nullptr;
|
||
};
|
||
|
||
const auto pragmaError = [decl](const char *msg) {
|
||
error(decl->loc, "`pragma(%s)` %s", decl->ident->toChars(), msg);
|
||
};
|
||
|
||
// pragma(LDC_intrinsic, "string") { funcdecl(s) }
|
||
if (ident == Id::LDC_intrinsic) {
|
||
if (!args || args->length != 1 || !parseStringExp(getFirstArg(), arg1str)) {
|
||
pragmaError("requires exactly 1 string literal parameter");
|
||
fatal();
|
||
}
|
||
|
||
// Recognize LDC-specific pragmas.
|
||
struct LdcIntrinsic {
|
||
std::string name;
|
||
LDCPragma pragma;
|
||
};
|
||
static LdcIntrinsic ldcIntrinsic[] = {
|
||
{"bitop.bt", LLVMbitop_bt}, {"bitop.btc", LLVMbitop_btc},
|
||
{"bitop.btr", LLVMbitop_btr}, {"bitop.bts", LLVMbitop_bts},
|
||
{"bitop.vld", LLVMbitop_vld}, {"bitop.vst", LLVMbitop_vst},
|
||
};
|
||
|
||
static std::string prefix = "ldc.";
|
||
size_t arg1str_length = strlen(arg1str);
|
||
if (arg1str_length > prefix.length() &&
|
||
std::equal(prefix.begin(), prefix.end(), arg1str)) {
|
||
// Got ldc prefix, binary search through ldcIntrinsic.
|
||
std::string name(arg1str + prefix.length());
|
||
size_t i = 0, j = sizeof(ldcIntrinsic) / sizeof(ldcIntrinsic[0]);
|
||
do {
|
||
size_t k = (i + j) / 2;
|
||
int cmp = name.compare(ldcIntrinsic[k].name);
|
||
if (!cmp) {
|
||
return ldcIntrinsic[k].pragma;
|
||
}
|
||
if (cmp < 0) {
|
||
j = k;
|
||
} else {
|
||
i = k + 1;
|
||
}
|
||
} while (i != j);
|
||
}
|
||
|
||
return LLVMintrinsic;
|
||
}
|
||
|
||
// pragma(LDC_global_crt_{c,d}tor [, priority]) { funcdecl(s) }
|
||
// pragma(crt_{con,de}structor [, priority]) { funcdecl(s) }
|
||
if (ident == Id::LDC_global_crt_ctor || ident == Id::LDC_global_crt_dtor ||
|
||
ident == Id::crt_constructor || ident == Id::crt_destructor) {
|
||
dinteger_t priority;
|
||
if (args) {
|
||
if (args->length != 1 || !parseIntExp(getFirstArg(), priority)) {
|
||
pragmaError("requires at most 1 integer literal parameter");
|
||
fatal();
|
||
}
|
||
if (priority > 65535) {
|
||
pragmaError("may not have a priority greater than 65535");
|
||
priority = 65535;
|
||
}
|
||
} else {
|
||
priority = 65535;
|
||
}
|
||
char buf[8];
|
||
snprintf(buf, 8, "%llu", static_cast<unsigned long long>(priority));
|
||
arg1str = strdup(buf);
|
||
return ident == Id::LDC_global_crt_ctor || ident == Id::crt_constructor
|
||
? LLVMglobal_crt_ctor
|
||
: LLVMglobal_crt_dtor;
|
||
}
|
||
|
||
// pragma(LDC_no_typeinfo) { typedecl(s) }
|
||
if (ident == Id::LDC_no_typeinfo) {
|
||
if (args && args->length > 0) {
|
||
pragmaError("takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMno_typeinfo;
|
||
}
|
||
|
||
// pragma(LDC_no_moduleinfo) ;
|
||
if (ident == Id::LDC_no_moduleinfo) {
|
||
if (args && args->length > 0) {
|
||
pragmaError("takes no parameters");
|
||
fatal();
|
||
}
|
||
sc->_module->noModuleInfo = true;
|
||
return LLVMignore;
|
||
}
|
||
|
||
// pragma(LDC_alloca) { funcdecl(s) }
|
||
if (ident == Id::LDC_alloca) {
|
||
if (args && args->length > 0) {
|
||
pragmaError("takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMalloca;
|
||
}
|
||
|
||
// pragma(LDC_va_start) { templdecl(s) }
|
||
if (ident == Id::LDC_va_start) {
|
||
if (args && args->length > 0) {
|
||
pragmaError("takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMva_start;
|
||
}
|
||
|
||
// pragma(LDC_va_copy) { funcdecl(s) }
|
||
if (ident == Id::LDC_va_copy) {
|
||
if (args && args->length > 0) {
|
||
pragmaError("takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMva_copy;
|
||
}
|
||
|
||
// pragma(LDC_va_end) { funcdecl(s) }
|
||
if (ident == Id::LDC_va_end) {
|
||
if (args && args->length > 0) {
|
||
pragmaError("takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMva_end;
|
||
}
|
||
|
||
// pragma(LDC_va_arg) { templdecl(s) }
|
||
if (ident == Id::LDC_va_arg) {
|
||
if (args && args->length > 0) {
|
||
pragmaError("takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMva_arg;
|
||
}
|
||
|
||
// pragma(LDC_fence) { funcdecl(s) }
|
||
if (ident == Id::LDC_fence) {
|
||
if (args && args->length > 0) {
|
||
pragmaError("takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMfence;
|
||
}
|
||
|
||
// pragma(LDC_atomic_load) { templdecl(s) }
|
||
if (ident == Id::LDC_atomic_load) {
|
||
if (args && args->length > 0) {
|
||
pragmaError("takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMatomic_load;
|
||
}
|
||
|
||
// pragma(LDC_atomic_store) { templdecl(s) }
|
||
if (ident == Id::LDC_atomic_store) {
|
||
if (args && args->length > 0) {
|
||
pragmaError("takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMatomic_store;
|
||
}
|
||
|
||
// pragma(LDC_atomic_cmp_xchg) { templdecl(s) }
|
||
if (ident == Id::LDC_atomic_cmp_xchg) {
|
||
if (args && args->length > 0) {
|
||
pragmaError("takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMatomic_cmp_xchg;
|
||
}
|
||
|
||
// pragma(LDC_atomic_rmw, "string") { templdecl(s) }
|
||
if (ident == Id::LDC_atomic_rmw) {
|
||
if (!args || args->length != 1 || !parseStringExp(getFirstArg(), arg1str)) {
|
||
pragmaError("requires exactly 1 string literal parameter");
|
||
fatal();
|
||
}
|
||
return LLVMatomic_rmw;
|
||
}
|
||
|
||
// pragma(LDC_verbose);
|
||
if (ident == Id::LDC_verbose) {
|
||
if (args && args->length > 0) {
|
||
pragmaError("takes no parameters");
|
||
fatal();
|
||
}
|
||
sc->_module->llvmForceLogging = true;
|
||
return LLVMignore;
|
||
}
|
||
|
||
// pragma(LDC_inline_asm) { templdecl(s) }
|
||
if (ident == Id::LDC_inline_asm) {
|
||
if (args && args->length > 0) {
|
||
pragmaError("takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMinline_asm;
|
||
}
|
||
|
||
// pragma(LDC_inline_ir) { templdecl(s) }
|
||
if (ident == Id::LDC_inline_ir) {
|
||
if (args && args->length > 0) {
|
||
pragmaError("takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMinline_ir;
|
||
}
|
||
|
||
// pragma(LDC_extern_weak) { vardecl(s) }
|
||
if (ident == Id::LDC_extern_weak) {
|
||
if (args && args->length > 0) {
|
||
pragmaError("takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMextern_weak;
|
||
}
|
||
|
||
// pragma(LDC_profile_instr, [true | false])
|
||
if (ident == Id::LDC_profile_instr) {
|
||
// checking of this pragma is done in DtoCheckProfileInstrPragma()
|
||
return LLVMprofile_instr;
|
||
}
|
||
|
||
return LLVMnone;
|
||
}
|
||
|
||
void DtoCheckPragma(PragmaDeclaration *decl, Dsymbol *s,
|
||
LDCPragma llvm_internal, const char *const arg1str) {
|
||
if (llvm_internal == LLVMnone || llvm_internal == LLVMignore ||
|
||
llvm_internal == LLVMprofile_instr) {
|
||
return;
|
||
}
|
||
|
||
Identifier *ident = decl->ident;
|
||
|
||
switch (llvm_internal) {
|
||
case LLVMintrinsic: {
|
||
int count = applyFunctionPragma(s, [=](FuncDeclaration *fd) {
|
||
fd->llvmInternal = llvm_internal;
|
||
fd->mangleOverride = {strlen(arg1str), arg1str};
|
||
});
|
||
count += applyTemplatePragma(s, [=](TemplateDeclaration *td) {
|
||
td->llvmInternal = llvm_internal;
|
||
td->intrinsicName = arg1str;
|
||
});
|
||
if (count != 1) {
|
||
error(s->loc,
|
||
"the `%s` pragma doesn't affect exactly 1 function/template "
|
||
"declaration",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
break;
|
||
}
|
||
|
||
case LLVMglobal_crt_ctor:
|
||
case LLVMglobal_crt_dtor: {
|
||
const int count = applyFunctionPragma(s, [=](FuncDeclaration *fd) {
|
||
auto tf = fd->type->isTypeFunction();
|
||
assert(tf);
|
||
|
||
bool isValid = false;
|
||
if (tf->next->ty != TY::Tvoid) {
|
||
error(fd->loc, "%s `%s` must return `void` for `pragma(%s)`",
|
||
fd->kind(), fd->toPrettyChars(), ident->toChars());
|
||
} else if (tf->parameterList.length() > 0) {
|
||
error(fd->loc, "%s `%s` must not take any parameters for `pragma(%s)`",
|
||
fd->kind(), fd->toPrettyChars(), ident->toChars());
|
||
} else if (fd->isThis()) {
|
||
error(fd->loc,
|
||
"%s `%s` cannot be a non-static member function for `pragma(%s)`",
|
||
fd->kind(), fd->toPrettyChars(), ident->toChars());
|
||
} else {
|
||
isValid = true;
|
||
}
|
||
|
||
if (!isValid) {
|
||
if (llvm_internal == LLVMglobal_crt_ctor) {
|
||
fd->isCrtCtor(false);
|
||
} else {
|
||
fd->isCrtDtor(false);
|
||
}
|
||
} else {
|
||
if (llvm_internal == LLVMglobal_crt_ctor) {
|
||
fd->isCrtCtor(true);
|
||
} else {
|
||
fd->isCrtDtor(true);
|
||
}
|
||
fd->priority = std::atoi(arg1str);
|
||
}
|
||
});
|
||
if (count != 1) {
|
||
error(s->loc,
|
||
"the `%s` pragma doesn't affect exactly 1 function declaration",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
break;
|
||
}
|
||
|
||
case LLVMatomic_rmw: {
|
||
const int count = applyTemplatePragma(s, [=](TemplateDeclaration *td) {
|
||
td->llvmInternal = llvm_internal;
|
||
td->intrinsicName = arg1str;
|
||
});
|
||
if (count != 1) {
|
||
error(s->loc,
|
||
"the `%s` pragma doesn't affect exactly 1 template declaration",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
break;
|
||
}
|
||
|
||
case LLVMva_start:
|
||
case LLVMva_arg:
|
||
case LLVMatomic_load:
|
||
case LLVMatomic_store:
|
||
case LLVMatomic_cmp_xchg: {
|
||
const int count = applyTemplatePragma(s, [=](TemplateDeclaration *td) {
|
||
if (td->parameters->length != 1) {
|
||
error(
|
||
s->loc,
|
||
"the `%s` pragma template must have exactly one template parameter",
|
||
ident->toChars());
|
||
fatal();
|
||
} else if (!td->onemember) {
|
||
error(s->loc, "the `%s` pragma template must have exactly one member",
|
||
ident->toChars());
|
||
fatal();
|
||
} else if (td->overnext || td->overroot) {
|
||
error(s->loc, "the `%s` pragma template must not be overloaded",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
td->llvmInternal = llvm_internal;
|
||
});
|
||
if (count != 1) {
|
||
error(s->loc,
|
||
"the `%s` pragma doesn't affect exactly 1 template declaration",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
break;
|
||
}
|
||
|
||
case LLVMalloca:
|
||
case LLVMva_copy:
|
||
case LLVMva_end:
|
||
case LLVMfence:
|
||
case LLVMbitop_bt:
|
||
case LLVMbitop_btc:
|
||
case LLVMbitop_btr:
|
||
case LLVMbitop_bts:
|
||
case LLVMbitop_vld:
|
||
case LLVMbitop_vst: {
|
||
const int count = applyFunctionPragma(s, [=](FuncDeclaration *fd) {
|
||
fd->llvmInternal = llvm_internal;
|
||
});
|
||
if (count != 1) {
|
||
error(s->loc,
|
||
"the `%s` pragma doesn't affect exactly 1 function declaration",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
break;
|
||
}
|
||
|
||
case LLVMno_typeinfo:
|
||
applyPragma<Dsymbol>(s, [](Dsymbol *s) { return s; },
|
||
[=](Dsymbol *s) { s->llvmInternal = llvm_internal; });
|
||
break;
|
||
|
||
case LLVMinline_asm: {
|
||
const int count = applyTemplatePragma(s, [=](TemplateDeclaration *td) {
|
||
if (td->parameters->length > 1) {
|
||
error(s->loc, "the `%s` pragma template must have exactly zero or one "
|
||
"template parameters",
|
||
ident->toChars());
|
||
fatal();
|
||
} else if (!td->onemember) {
|
||
error(s->loc, "the `%s` pragma template must have exactly one member",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
td->llvmInternal = llvm_internal;
|
||
});
|
||
if (count != 1) {
|
||
error(s->loc,
|
||
"the `%s` pragma doesn't affect exactly 1 template declaration",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
break;
|
||
}
|
||
|
||
case LLVMinline_ir: {
|
||
DtoCheckInlineIRPragma(ident, s);
|
||
const int count = applyTemplatePragma(s, [=](TemplateDeclaration *td) {
|
||
if (!td->onemember) {
|
||
error(s->loc, "the `%s` pragma template must have exactly one member",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
td->llvmInternal = llvm_internal;
|
||
});
|
||
if (count != 1) {
|
||
error(s->loc,
|
||
"the `%s` pragma doesn't affect exactly 1 template declaration",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
break;
|
||
}
|
||
|
||
case LLVMextern_weak: {
|
||
int count = applyVariablePragma(s, [=](VarDeclaration *vd) {
|
||
if (!vd->isDataseg() || !(vd->storage_class & STCextern)) {
|
||
error(s->loc, "`%s` requires storage class `extern`", ident->toChars());
|
||
fatal();
|
||
}
|
||
|
||
// It seems like the interaction between weak symbols and thread-local
|
||
// storage is not well-defined (the address of an undefined weak TLS
|
||
// symbol is non-zero on the ELF static TLS model on Linux x86_64).
|
||
// Thus, just disallow this altogether.
|
||
if (vd->isThreadlocal()) {
|
||
error(s->loc, "`%s` cannot be applied to thread-local variable `%s`",
|
||
ident->toChars(), vd->toPrettyChars());
|
||
fatal();
|
||
}
|
||
vd->llvmInternal = llvm_internal;
|
||
});
|
||
count += applyFunctionPragma(s, [=](FuncDeclaration *fd) {
|
||
if (fd->fbody) {
|
||
error(s->loc, "`%s` cannot be applied to function definitions", ident->toChars());
|
||
fatal();
|
||
}
|
||
fd->llvmInternal = llvm_internal;
|
||
});
|
||
|
||
if (count == 0) {
|
||
error(s->loc,
|
||
"the `%s` pragma doesn't affect any variable or function "
|
||
"declarations",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
break;
|
||
}
|
||
|
||
default:
|
||
warning(s->loc,
|
||
"the LDC specific pragma `%s` is not yet implemented, ignoring",
|
||
ident->toChars());
|
||
}
|
||
}
|
||
|
||
bool DtoIsIntrinsic(FuncDeclaration *fd) {
|
||
return fd->llvmInternal == LLVMintrinsic || DtoIsMagicIntrinsic(fd);
|
||
}
|
||
|
||
bool DtoIsMagicIntrinsic(FuncDeclaration *fd) {
|
||
// note: keep in sync with DtoLowerMagicIntrinsic()
|
||
switch (fd->llvmInternal) {
|
||
case LLVMva_start: // doesn't map to an instruction, but handled as magic intrinsic by LDC
|
||
case LLVMva_copy: // ditto
|
||
case LLVMva_arg:
|
||
case LLVMva_end: // ditto
|
||
case LLVMalloca:
|
||
case LLVMfence:
|
||
case LLVMatomic_store:
|
||
case LLVMatomic_load:
|
||
case LLVMatomic_cmp_xchg:
|
||
case LLVMatomic_rmw:
|
||
case LLVMbitop_bt:
|
||
case LLVMbitop_btc:
|
||
case LLVMbitop_btr:
|
||
case LLVMbitop_bts:
|
||
case LLVMbitop_vld:
|
||
case LLVMbitop_vst:
|
||
return true;
|
||
|
||
default:
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// pragma(LDC_profile_instr, [true | false])
|
||
// Return false if an error occurred.
|
||
bool DtoCheckProfileInstrPragma(Expression *arg, bool &value) {
|
||
return parseBoolExp(arg, value);
|
||
}
|