ldc/gen/pragma.cpp
Martin Kinkelin ef72dedd7c
Refactor away LDC-specific FuncDeclaration::intrinsicName (#4595)
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.
2024-03-21 16:50:06 +01:00

609 lines
17 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//===-- 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);
}