Don't forward-declare magic intrinsics mapping to instructions

It's ugly in general, and apparently causes LTO issues on Windows.

This required a larger refactoring.
This commit is contained in:
Martin Kinkelin 2024-03-16 16:02:51 +01:00
parent be00cbb189
commit 078f0532ca
6 changed files with 39 additions and 92 deletions

View file

@ -51,4 +51,3 @@ extern (C++) LDCPragma DtoGetPragma(Scope* sc, PragmaDeclaration decl, ref const
extern (C++) void DtoCheckPragma(PragmaDeclaration decl, Dsymbol sym, LDCPragma llvm_internal, const char* arg1str);
extern (C++) bool DtoCheckProfileInstrPragma(Expression arg, ref bool value);
extern (C++) bool DtoIsIntrinsic(FuncDeclaration fd);
extern (C++) bool DtoIsVaIntrinsic(FuncDeclaration fd);

View file

@ -293,37 +293,7 @@ llvm::FunctionType *DtoFunctionType(Type *type, IrFuncTy &irFty, Type *thistype,
////////////////////////////////////////////////////////////////////////////////
static llvm::FunctionType *DtoVaFunctionType(FuncDeclaration *fdecl) {
IrFuncTy &irFty = getIrFunc(fdecl, true)->irFty;
if (irFty.funcType) {
return irFty.funcType;
}
irFty.ret = new IrFuncTyArg(Type::tvoid, false);
irFty.args.push_back(new IrFuncTyArg(pointerTo(Type::tvoid), false));
if (fdecl->llvmInternal == LLVMva_start) {
irFty.funcType = GET_INTRINSIC_DECL(vastart)->getFunctionType();
} else if (fdecl->llvmInternal == LLVMva_copy) {
irFty.funcType = GET_INTRINSIC_DECL(vacopy)->getFunctionType();
irFty.args.push_back(new IrFuncTyArg(pointerTo(Type::tvoid), false));
} else if (fdecl->llvmInternal == LLVMva_end) {
irFty.funcType = GET_INTRINSIC_DECL(vaend)->getFunctionType();
}
assert(irFty.funcType);
return irFty.funcType;
}
////////////////////////////////////////////////////////////////////////////////
llvm::FunctionType *DtoFunctionType(FuncDeclaration *fdecl) {
// handle for C vararg intrinsics
if (DtoIsVaIntrinsic(fdecl)) {
return DtoVaFunctionType(fdecl);
}
Type *dthis = nullptr, *dnest = nullptr;
if (fdecl->ident == Id::ensure || fdecl->ident == Id::require) {
@ -366,25 +336,6 @@ llvm::FunctionType *DtoFunctionType(FuncDeclaration *fdecl) {
////////////////////////////////////////////////////////////////////////////////
static llvm::Function *DtoDeclareVaFunction(FuncDeclaration *fdecl) {
DtoVaFunctionType(fdecl);
llvm::Function *func = nullptr;
if (fdecl->llvmInternal == LLVMva_start) {
func = GET_INTRINSIC_DECL(vastart);
} else if (fdecl->llvmInternal == LLVMva_copy) {
func = GET_INTRINSIC_DECL(vacopy);
} else if (fdecl->llvmInternal == LLVMva_end) {
func = GET_INTRINSIC_DECL(vaend);
}
assert(func);
getIrFunc(fdecl)->setLLVMFunc(func);
return func;
}
////////////////////////////////////////////////////////////////////////////////
void DtoResolveFunction(FuncDeclaration *fdecl, const bool willDeclare) {
if ((!global.params.useUnitTests || !fdecl->type) &&
fdecl->isUnitTestDeclaration()) {
@ -412,21 +363,7 @@ void DtoResolveFunction(FuncDeclaration *fdecl, const bool willDeclare) {
if (TemplateInstance *tinst = fdecl->parent->isTemplateInstance()) {
if (TemplateDeclaration *tempdecl =
tinst->tempdecl->isTemplateDeclaration()) {
if (tempdecl->llvmInternal == LLVMva_arg) {
Logger::println("magic va_arg found");
fdecl->llvmInternal = LLVMva_arg;
fdecl->ir->setDefined();
return; // this gets mapped to an instruction so a declaration makes
// no sense
}
if (tempdecl->llvmInternal == LLVMva_start) {
Logger::println("magic va_start found");
fdecl->llvmInternal = LLVMva_start;
} else if (tempdecl->llvmInternal == LLVMintrinsic) {
Logger::println("overloaded intrinsic found");
assert(fdecl->llvmInternal == LLVMintrinsic);
assert(fdecl->mangleOverride.length);
} else if (tempdecl->llvmInternal == LLVMinline_asm) {
if (tempdecl->llvmInternal == LLVMinline_asm) {
Logger::println("magic inline asm found");
TypeFunction *tf = static_cast<TypeFunction *>(fdecl->type);
if (tf->parameterList.varargs != VARARGvariadic ||
@ -436,13 +373,13 @@ void DtoResolveFunction(FuncDeclaration *fdecl, const bool willDeclare) {
"variadic with no explicit parameters");
fatal();
}
fdecl->llvmInternal = LLVMinline_asm;
assert(fdecl->llvmInternal == LLVMinline_asm);
fdecl->ir->setDefined();
return; // this gets mapped to a special inline asm call, no point in
// going on.
} else if (tempdecl->llvmInternal == LLVMinline_ir) {
Logger::println("magic inline ir found");
fdecl->llvmInternal = LLVMinline_ir;
assert(fdecl->llvmInternal == LLVMinline_ir);
fdecl->_linkage = LINK::c;
Type *type = fdecl->type;
assert(type->ty == TY::Tfunction);
@ -457,6 +394,13 @@ void DtoResolveFunction(FuncDeclaration *fdecl, const bool willDeclare) {
}
}
// magic intrinsics are mapped to instructions, no point in fwd-declaring some
// non-existing function
if (DtoIsMagicIntrinsic(fdecl)) {
fdecl->ir->setDefined();
return;
}
DtoFunctionType(fdecl);
IF_LOG Logger::println("DtoResolveFunction(%s): %s", fdecl->toPrettyChars(),
@ -610,24 +554,19 @@ void DtoDeclareFunction(FuncDeclaration *fdecl, const bool willDefine) {
// create IrFunction
IrFunction *irFunc = getIrFunc(fdecl, true);
LLFunction *vafunc = nullptr;
if (DtoIsVaIntrinsic(fdecl)) {
vafunc = DtoDeclareVaFunction(fdecl);
}
// Calling convention.
//
// DMD treats _Dmain as having C calling convention and this has been
// hardcoded into druntime, even if the frontend type has D linkage (Bugzilla
// issue 9028).
const bool forceC = vafunc || DtoIsIntrinsic(fdecl) || fdecl->isMain();
const bool forceC = DtoIsIntrinsic(fdecl) || fdecl->isMain();
// mangled name
const auto irMangle = getIRMangledName(fdecl, forceC ? LINK::c : f->linkage);
// construct function
LLFunctionType *functype = DtoFunctionType(fdecl);
LLFunction *func = vafunc ? vafunc : gIR->module.getFunction(irMangle);
LLFunction *func = gIR->module.getFunction(irMangle);
if (!func) {
// All function declarations are "external" - any other linkage type
// is set when actually defining the function, except extern_weak.

View file

@ -1576,9 +1576,8 @@ DValue *DtoSymbolAddress(const Loc &loc, Type *type, Declaration *decl) {
fatal();
}
DtoResolveFunction(fdecl);
const auto llValue =
fdecl->llvmInternal != LLVMva_arg ? DtoCallee(fdecl) : nullptr;
return new DFuncValue(fdecl, llValue);
assert(!DtoIsMagicIntrinsic(fdecl));
return new DFuncValue(fdecl, DtoCallee(fdecl));
}
if (SymbolDeclaration *sdecl = decl->isSymbolDeclaration()) {

View file

@ -452,6 +452,7 @@ void DtoCheckPragma(PragmaDeclaration *decl, Dsymbol *s,
fatal();
}
td->llvmInternal = llvm_internal;
td->onemember->llvmInternal = llvm_internal;
});
if (count != 1) {
error(s->loc,
@ -502,6 +503,7 @@ void DtoCheckPragma(PragmaDeclaration *decl, Dsymbol *s,
fatal();
}
td->llvmInternal = llvm_internal;
td->onemember->llvmInternal = llvm_internal;
});
if (count != 1) {
error(s->loc,
@ -515,7 +517,13 @@ void DtoCheckPragma(PragmaDeclaration *decl, Dsymbol *s,
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;
td->onemember->llvmInternal = llvm_internal;
});
if (count != 1) {
error(s->loc,
@ -570,8 +578,16 @@ void DtoCheckPragma(PragmaDeclaration *decl, Dsymbol *s,
}
bool DtoIsIntrinsic(FuncDeclaration *fd) {
return fd->llvmInternal == LLVMintrinsic || DtoIsMagicIntrinsic(fd);
}
bool DtoIsMagicIntrinsic(FuncDeclaration *fd) {
// note: keep in sync with DtoLowerMagicIntrinsic()
switch (fd->llvmInternal) {
case LLVMintrinsic:
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:
@ -587,15 +603,10 @@ bool DtoIsIntrinsic(FuncDeclaration *fd) {
return true;
default:
return DtoIsVaIntrinsic(fd);
return false;
}
}
bool DtoIsVaIntrinsic(FuncDeclaration *fd) {
return (fd->llvmInternal == LLVMva_start || fd->llvmInternal == LLVMva_copy ||
fd->llvmInternal == LLVMva_end);
}
// pragma(LDC_profile_instr, [true | false])
// Return false if an error occurred.
bool DtoCheckProfileInstrPragma(Expression *arg, bool &value) {

View file

@ -56,4 +56,4 @@ void DtoCheckPragma(PragmaDeclaration *decl, Dsymbol *sym, LDCPragma llvm_intern
const char * const arg1str);
bool DtoCheckProfileInstrPragma(Expression *arg, bool &value);
bool DtoIsIntrinsic(FuncDeclaration *fd);
bool DtoIsVaIntrinsic(FuncDeclaration *fd);
bool DtoIsMagicIntrinsic(FuncDeclaration *fd);

View file

@ -710,7 +710,7 @@ public:
auto &PGO = gIR->funcGen().pgo;
PGO.setCurrentStmt(e);
// handle magic inline asm
// handle magic intrinsics and inline asm/IR
if (auto ve = e->e1->isVarExp()) {
if (auto fd = ve->var->isFuncDeclaration()) {
if (fd->llvmInternal == LLVMinline_asm) {
@ -719,6 +719,10 @@ public:
if (fd->llvmInternal == LLVMinline_ir) {
return DtoInlineIRExpr(e->loc, fd, e->arguments, sretPointer);
}
DValue *result = nullptr;
if (DtoLowerMagicIntrinsic(p, fd, e, result))
return result;
}
}
@ -767,13 +771,8 @@ public:
// get func value if any
DFuncValue *dfnval = fnval->isFunc();
// handle magic intrinsics (mapping to instructions)
if (dfnval && dfnval->func) {
FuncDeclaration *fndecl = dfnval->func;
DValue *result = nullptr;
if (DtoLowerMagicIntrinsic(p, fndecl, e, result))
return result;
assert(!DtoIsMagicIntrinsic(dfnval->func));
}
DValue *result =