mirror of
https://github.com/ldc-developers/ldc.git
synced 2025-04-28 14:10:42 +03:00
574 lines
16 KiB
C++
574 lines
16 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 "pragma.h"
|
||
#include "attrib.h"
|
||
#include "declaration.h"
|
||
#include "expression.h"
|
||
#include "id.h"
|
||
#include "module.h"
|
||
#include "scope.h"
|
||
#include "template.h"
|
||
#include "llvm/Support/CommandLine.h"
|
||
|
||
static bool parseStringExp(Expression* e, std::string& res)
|
||
{
|
||
StringExp *s = NULL;
|
||
|
||
e = e->optimize(WANTvalue);
|
||
if (e->op == TOKstring && (s = static_cast<StringExp *>(e)))
|
||
{
|
||
char* str = static_cast<char*>(s->string);
|
||
res = str;
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
static bool parseIntExp(Expression* e, dinteger_t& res)
|
||
{
|
||
IntegerExp *i = NULL;
|
||
|
||
e = e->optimize(WANTvalue);
|
||
if (e->op == TOKint64 && (i = static_cast<IntegerExp *>(e)))
|
||
{
|
||
res = i->getInteger();
|
||
return true;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
Pragma DtoGetPragma(Scope *sc, PragmaDeclaration *decl, std::string &arg1str)
|
||
{
|
||
Identifier *ident = decl->ident;
|
||
Expressions *args = decl->args;
|
||
Expression *expr = (args && args->dim > 0) ? (*args)[0]->semantic(sc) : 0;
|
||
|
||
// pragma(LDC_intrinsic, "string") { funcdecl(s) }
|
||
if (ident == Id::LDC_intrinsic)
|
||
{
|
||
if (!args || args->dim != 1 || !parseStringExp(expr, arg1str))
|
||
{
|
||
error(Loc(), "requires exactly 1 string literal parameter");
|
||
fatal();
|
||
}
|
||
|
||
// Recognize LDC-specific pragmas.
|
||
struct LdcIntrinsic
|
||
{
|
||
std::string name;
|
||
Pragma pragma;
|
||
};
|
||
static LdcIntrinsic ldcIntrinsic[] = {
|
||
{ "bitop.bt", LLVMbitop_bt },
|
||
{ "bitop.btc", LLVMbitop_btc },
|
||
{ "bitop.btr", LLVMbitop_btr },
|
||
{ "bitop.bts", LLVMbitop_bts },
|
||
};
|
||
|
||
static std::string prefix = "ldc.";
|
||
if (arg1str.length() > prefix.length() &&
|
||
std::equal(prefix.begin(), prefix.end(), arg1str.begin()))
|
||
{
|
||
// Got ldc prefix, binary search through ldcIntrinsic.
|
||
std::string name(arg1str.begin() + prefix.length(), arg1str.end());
|
||
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;
|
||
else if (cmp < 0)
|
||
j = k;
|
||
else
|
||
i = k + 1;
|
||
}
|
||
while (i != j);
|
||
}
|
||
|
||
return LLVMintrinsic;
|
||
}
|
||
|
||
// pragma(LDC_global_crt_ctor [, priority]) { funcdecl(s) }
|
||
else if (ident == Id::LDC_global_crt_ctor || ident == Id::LDC_global_crt_dtor)
|
||
{
|
||
dinteger_t priority;
|
||
if (args)
|
||
{
|
||
if (args->dim != 1 || !parseIntExp(expr, priority))
|
||
{
|
||
error(Loc(), "requires at most 1 integer literal parameter");
|
||
fatal();
|
||
}
|
||
if (priority > 65535)
|
||
{
|
||
error(Loc(), "priority may not be greater then 65535");
|
||
priority = 65535;
|
||
}
|
||
}
|
||
else
|
||
priority = 65535;
|
||
char buf[8];
|
||
sprintf(buf, "%lu", priority);
|
||
arg1str = std::string(buf);
|
||
return ident == Id::LDC_global_crt_ctor ? LLVMglobal_crt_ctor : LLVMglobal_crt_dtor;
|
||
}
|
||
|
||
// pragma(LDC_no_typeinfo) { typedecl(s) }
|
||
else if (ident == Id::LDC_no_typeinfo)
|
||
{
|
||
if (args && args->dim > 0)
|
||
{
|
||
error(Loc(), "takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMno_typeinfo;
|
||
}
|
||
|
||
// pragma(LDC_no_moduleinfo) ;
|
||
else if (ident == Id::LDC_no_moduleinfo)
|
||
{
|
||
if (args && args->dim > 0)
|
||
{
|
||
error(Loc(), "takes no parameters");
|
||
fatal();
|
||
}
|
||
sc->module->noModuleInfo = true;
|
||
return LLVMignore;
|
||
}
|
||
|
||
// pragma(LDC_alloca) { funcdecl(s) }
|
||
else if (ident == Id::LDC_alloca)
|
||
{
|
||
if (args && args->dim > 0)
|
||
{
|
||
error(Loc(), "takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMalloca;
|
||
}
|
||
|
||
// pragma(LDC_va_start) { templdecl(s) }
|
||
else if (ident == Id::LDC_va_start)
|
||
{
|
||
if (args && args->dim > 0)
|
||
{
|
||
error(Loc(), "takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMva_start;
|
||
}
|
||
|
||
// pragma(LDC_va_copy) { funcdecl(s) }
|
||
else if (ident == Id::LDC_va_copy)
|
||
{
|
||
if (args && args->dim > 0)
|
||
{
|
||
error(Loc(), "takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMva_copy;
|
||
}
|
||
|
||
// pragma(LDC_va_end) { funcdecl(s) }
|
||
else if (ident == Id::LDC_va_end)
|
||
{
|
||
if (args && args->dim > 0)
|
||
{
|
||
error(Loc(), "takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMva_end;
|
||
}
|
||
|
||
// pragma(LDC_va_arg) { templdecl(s) }
|
||
else if (ident == Id::LDC_va_arg)
|
||
{
|
||
if (args && args->dim > 0)
|
||
{
|
||
error(Loc(), "takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMva_arg;
|
||
}
|
||
|
||
// pragma(LDC_fence) { funcdecl(s) }
|
||
else if (ident == Id::LDC_fence)
|
||
{
|
||
if (args && args->dim > 0)
|
||
{
|
||
error(Loc(), "takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMfence;
|
||
}
|
||
|
||
// pragma(LDC_atomic_load) { templdecl(s) }
|
||
else if (ident == Id::LDC_atomic_load)
|
||
{
|
||
if (args && args->dim > 0)
|
||
{
|
||
error(Loc(), "takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMatomic_load;
|
||
}
|
||
|
||
// pragma(LDC_atomic_store) { templdecl(s) }
|
||
else if (ident == Id::LDC_atomic_store)
|
||
{
|
||
if (args && args->dim > 0)
|
||
{
|
||
error(Loc(), "takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMatomic_store;
|
||
}
|
||
|
||
// pragma(LDC_atomic_cmp_xchg) { templdecl(s) }
|
||
else if (ident == Id::LDC_atomic_cmp_xchg)
|
||
{
|
||
if (args && args->dim > 0)
|
||
{
|
||
error(Loc(), "takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMatomic_cmp_xchg;
|
||
}
|
||
|
||
// pragma(LDC_atomic_rmw, "string") { templdecl(s) }
|
||
else if (ident == Id::LDC_atomic_rmw)
|
||
{
|
||
if (!args || args->dim != 1 || !parseStringExp(expr, arg1str))
|
||
{
|
||
error(Loc(), "requires exactly 1 string literal parameter");
|
||
fatal();
|
||
}
|
||
return LLVMatomic_rmw;
|
||
}
|
||
|
||
// pragma(LDC_verbose);
|
||
else if (ident == Id::LDC_verbose)
|
||
{
|
||
if (args && args->dim > 0)
|
||
{
|
||
error(Loc(), "takes no parameters");
|
||
fatal();
|
||
}
|
||
sc->module->llvmForceLogging = true;
|
||
return LLVMignore;
|
||
}
|
||
|
||
// pragma(LDC_inline_asm) { templdecl(s) }
|
||
else if (ident == Id::LDC_inline_asm)
|
||
{
|
||
if (args && args->dim > 0)
|
||
{
|
||
error(Loc(), "takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMinline_asm;
|
||
}
|
||
|
||
// pragma(LDC_inline_ir) { templdecl(s) }
|
||
else if (ident == Id::LDC_inline_ir)
|
||
{
|
||
if (args && args->dim > 0)
|
||
{
|
||
error(Loc(), "takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMinline_ir;
|
||
}
|
||
|
||
// pragma(LDC_extern_weak) { vardecl(s) }
|
||
else if (ident == Id::LDC_extern_weak)
|
||
{
|
||
if (args && args->dim > 0)
|
||
{
|
||
error(Loc(), "takes no parameters");
|
||
fatal();
|
||
}
|
||
return LLVMextern_weak;
|
||
}
|
||
|
||
return LLVMnone;
|
||
}
|
||
|
||
void DtoCheckPragma(PragmaDeclaration *decl, Dsymbol *s,
|
||
Pragma llvm_internal, const std::string &arg1str)
|
||
{
|
||
if (llvm_internal == LLVMnone || llvm_internal == LLVMignore)
|
||
return;
|
||
|
||
if (s->llvmInternal)
|
||
{
|
||
error(Loc(), "multiple LDC specific pragmas not allowed not affect the same "
|
||
"declaration ('%s' at '%s')", s->toChars(), s->loc.toChars());
|
||
fatal();
|
||
}
|
||
|
||
Identifier *ident = decl->ident;
|
||
|
||
switch(llvm_internal)
|
||
{
|
||
case LLVMintrinsic:
|
||
if (FuncDeclaration* fd = s->isFuncDeclaration())
|
||
{
|
||
fd->llvmInternal = llvm_internal;
|
||
fd->intrinsicName = arg1str;
|
||
fd->mangleOverride = strdup(fd->intrinsicName.c_str());
|
||
}
|
||
else if (TemplateDeclaration* td = s->isTemplateDeclaration())
|
||
{
|
||
td->llvmInternal = llvm_internal;
|
||
td->intrinsicName = arg1str;
|
||
}
|
||
else
|
||
{
|
||
error(s->loc, "the '%s' pragma is only allowed on function or template declarations",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
break;
|
||
|
||
case LLVMglobal_crt_ctor:
|
||
case LLVMglobal_crt_dtor:
|
||
if (FuncDeclaration* fd = s->isFuncDeclaration())
|
||
{
|
||
assert(fd->type->ty == Tfunction);
|
||
TypeFunction* type = static_cast<TypeFunction*>(fd->type);
|
||
Type* retType = type->next;
|
||
if (retType->ty != Tvoid || type->parameters->dim > 0 || (
|
||
fd->isAggregateMember()
|
||
&& !fd->isStatic())) {
|
||
error(s->loc, "the '%s' pragma is only allowed on void functions which take no arguments",
|
||
ident->toChars());
|
||
fd->llvmInternal = LLVMnone;
|
||
break;
|
||
}
|
||
|
||
fd->llvmInternal = llvm_internal;
|
||
fd->priority = std::atoi(arg1str.c_str());
|
||
}
|
||
else
|
||
{
|
||
error(s->loc, "the '%s' pragma is only allowed on function declarations",
|
||
ident->toChars());
|
||
s->llvmInternal = LLVMnone;
|
||
}
|
||
break;
|
||
|
||
case LLVMatomic_rmw:
|
||
if (TemplateDeclaration* td = s->isTemplateDeclaration())
|
||
{
|
||
td->llvmInternal = llvm_internal;
|
||
td->intrinsicName = arg1str;
|
||
}
|
||
else
|
||
{
|
||
error(s->loc, "the '%s' pragma is only allowed on template declarations",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
break;
|
||
|
||
case LLVMva_start:
|
||
case LLVMva_arg:
|
||
case LLVMatomic_load:
|
||
case LLVMatomic_store:
|
||
case LLVMatomic_cmp_xchg:
|
||
if (TemplateDeclaration* td = s->isTemplateDeclaration())
|
||
{
|
||
if (td->parameters->dim != 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;
|
||
}
|
||
else
|
||
{
|
||
error(s->loc, "the '%s' pragma is only allowed on template declarations",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
break;
|
||
|
||
case LLVMva_copy:
|
||
case LLVMva_end:
|
||
case LLVMfence:
|
||
case LLVMbitop_bt:
|
||
case LLVMbitop_btc:
|
||
case LLVMbitop_btr:
|
||
case LLVMbitop_bts:
|
||
if (FuncDeclaration* fd = s->isFuncDeclaration())
|
||
{
|
||
fd->llvmInternal = llvm_internal;
|
||
}
|
||
else
|
||
{
|
||
error(s->loc, "the '%s' pragma is only allowed on function declarations",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
break;
|
||
|
||
case LLVMno_typeinfo:
|
||
s->llvmInternal = llvm_internal;
|
||
break;
|
||
|
||
case LLVMalloca:
|
||
if (FuncDeclaration* fd = s->isFuncDeclaration())
|
||
{
|
||
fd->llvmInternal = llvm_internal;
|
||
}
|
||
else
|
||
{
|
||
error(s->loc, "the '%s' pragma must only be used on function declarations "
|
||
"of type 'void* function(uint nbytes)'", ident->toChars());
|
||
fatal();
|
||
}
|
||
break;
|
||
|
||
case LLVMinline_asm:
|
||
if (TemplateDeclaration* td = s->isTemplateDeclaration())
|
||
{
|
||
if (td->parameters->dim > 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;
|
||
}
|
||
else
|
||
{
|
||
error(s->loc, "the '%s' pragma is only allowed on template declarations",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
break;
|
||
|
||
case LLVMinline_ir:
|
||
if (TemplateDeclaration* td = s->isTemplateDeclaration())
|
||
{
|
||
Dsymbol* member = td->onemember;
|
||
if (!member)
|
||
{
|
||
error(s->loc, "the '%s' pragma template must have exactly one member",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
FuncDeclaration* fun = member->isFuncDeclaration();
|
||
if (!fun)
|
||
{
|
||
error(s->loc, "the '%s' pragma template's member must be a function declaration",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
|
||
TemplateParameters& params = *td->parameters;
|
||
bool valid_params =
|
||
params.dim == 3 && params[1]->isTemplateTypeParameter() &&
|
||
params[2]->isTemplateTupleParameter();
|
||
|
||
if(valid_params)
|
||
{
|
||
TemplateValueParameter* p0 = params[0]->isTemplateValueParameter();
|
||
valid_params = valid_params && p0 && p0->valType == Type::tstring;
|
||
}
|
||
|
||
if(!valid_params)
|
||
{
|
||
error(s->loc, "the '%s' pragma template must have exactly three parameters: "
|
||
"a string, a type and a type tuple", ident->toChars());
|
||
fatal();
|
||
}
|
||
|
||
td->llvmInternal = llvm_internal;
|
||
}
|
||
else
|
||
{
|
||
error(s->loc, "the '%s' pragma is only allowed on template declarations",
|
||
ident->toChars());
|
||
fatal();
|
||
}
|
||
break;
|
||
|
||
case LLVMextern_weak:
|
||
if (VarDeclaration* vd = s->isVarDeclaration())
|
||
{
|
||
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;
|
||
}
|
||
else
|
||
{
|
||
// Currently, things like "pragma(LDC_extern_weak) extern int foo;"
|
||
// fail because 'extern' creates an intermediate
|
||
// StorageClassDeclaration. This might eventually be fixed by making
|
||
// extern_weak a proper storage class.
|
||
error(s->loc, "the '%s' pragma can only be specified directly on "
|
||
"variable declarations for now", 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 || DtoIsVaIntrinsic(fd));
|
||
}
|
||
|
||
bool DtoIsVaIntrinsic(FuncDeclaration *fd)
|
||
{
|
||
return (fd->llvmInternal == LLVMva_start ||
|
||
fd->llvmInternal == LLVMva_copy ||
|
||
fd->llvmInternal == LLVMva_end);
|
||
}
|