Skip to content

Commit 04a6c8b

Browse files
authored
Merge pull request #5041 from CyberShadow/naked-llvm-ir-refactor
Emit naked functions as LLVM IR with inline asm instead of module asm
2 parents c8305d0 + 3206d53 commit 04a6c8b

14 files changed

Lines changed: 368 additions & 344 deletions

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
#### Big news
44
- Support for [LLVM 22](https://releases.llvm.org/22.1.0/docs/ReleaseNotes.html). The prebuilt packages use v22.1.2. (#5097, #5102)
55
- Minimum LLVM version raised to 18. (#5094)
6+
- DMD-style inline assembly: `asm { naked; }` is now much less of a special case wrt. codegen - such naked functions are now emitted as if `asm { naked; }` was replaced with the `@naked` function UDA. IR-wise, they are not emitted as module-level asm blobs anymore, but regular IR functions. This should lift a few former `asm { naked; }`-specific restrictions and fixed at least one LTO issue. (#5041)
67
- Predefined version `LDC_LLVM_*` now only contains the LLVM major version, i.e., former `version (LDC_LLVM_1801)` with LLVM v18.1 is now `version (LDC_LLVM_18)`. Use `ldc.intrinsics.LLVM_version` for backwards compatibility if really needed. (#5109)
78

89
#### Platform support

dmd/declaration.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -581,6 +581,9 @@ class FuncDeclaration : public Declaration
581581
VarDeclaration *v_argptr; // '_argptr' variable
582582
VarDeclarations *parameters; // Array of VarDeclaration's for parameters
583583
DsymbolTable *labtab; // statement label symbol table
584+
#if IN_LLVM
585+
Identifiers *asmLabels; // identifiers of labels defined in DMD-style inline assembly
586+
#endif
584587
Dsymbol *overnext; // next in overload list
585588
FuncDeclaration *overnext0; // next in overload list (only used during IFTI)
586589
Loc endloc; // location of closing curly bracket

dmd/func.d

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,10 @@ version (IN_LLVM)
256256
VarDeclaration v_argptr; /// '_argptr' variable
257257
VarDeclarations* parameters; /// Array of VarDeclaration's for parameters
258258
DsymbolTable labtab; /// statement label symbol table
259+
version (IN_LLVM)
260+
{
261+
Identifiers* asmLabels; /// identifiers of labels defined in DMD-style inline assembly
262+
}
259263
Dsymbol overnext; /// next in overload list
260264
FuncDeclaration overnext0; /// next in overload list (only used during IFTI)
261265
Loc endloc; /// location of closing curly bracket

dmd/statementsem.d

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3695,6 +3695,13 @@ version (IN_LLVM)
36953695
if (auto ls = s.isLabelStatement())
36963696
{
36973697
sc.func.searchLabel(ls.ident, ls.loc);
3698+
3699+
version (IN_LLVM)
3700+
{
3701+
if (!sc.func.asmLabels)
3702+
sc.func.asmLabels = new Identifiers();
3703+
sc.func.asmLabels.push(ls.ident);
3704+
}
36983705
}
36993706
}
37003707
}

gen/asm-x86.h

Lines changed: 11 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -2340,57 +2340,15 @@ struct AsmProcessor {
23402340
AsmCode *asmcode, AsmArgMode mode = Mode_Input) {
23412341
using namespace dmd;
23422342

2343-
if (sc->func->isNaked()) {
2344-
switch (type) {
2345-
case Arg_Integer:
2346-
if (e->type->isUnsigned()) {
2347-
insnTemplate << "$" << e->toUInteger();
2348-
} else {
2343+
if (type == Arg_Integer) {
2344+
if (e->type->isUnsigned()) {
2345+
insnTemplate << "$$" << e->toUInteger();
2346+
} else {
23492347
#ifndef ASM_X86_64
2350-
insnTemplate << "$" << static_cast<sinteger_t>(e->toInteger());
2348+
insnTemplate << "$$" << static_cast<sinteger_t>(e->toInteger());
23512349
#else
2352-
insnTemplate << "$" << e->toInteger();
2350+
insnTemplate << "$$" << e->toInteger();
23532351
#endif
2354-
}
2355-
break;
2356-
2357-
case Arg_Pointer:
2358-
error(stmt->loc, "unsupported pointer reference to `%s` in naked asm",
2359-
e->toChars());
2360-
break;
2361-
2362-
case Arg_Memory:
2363-
// Peel off one layer of explicitly taking the address, if present.
2364-
if (auto ae = e->isAddrExp()) {
2365-
e = ae->e1;
2366-
}
2367-
2368-
if (auto v = e->isVarExp()) {
2369-
if (VarDeclaration *vd = v->var->isVarDeclaration()) {
2370-
if (!vd->isDataseg()) {
2371-
error(stmt->loc, "only global variables can be referenced by "
2372-
"identifier in naked asm");
2373-
break;
2374-
}
2375-
2376-
// print out the mangle
2377-
if (prependExtraUnderscore(vd->resolvedLinkage())) {
2378-
insnTemplate << "_";
2379-
}
2380-
OutBuffer buf;
2381-
mangleToBuffer(vd, buf);
2382-
insnTemplate << buf.peekChars();
2383-
getIrGlobal(vd, true)->nakedUse = true;
2384-
break;
2385-
}
2386-
}
2387-
error(stmt->loc, "unsupported memory reference to `%s` in naked asm",
2388-
e->toChars());
2389-
break;
2390-
2391-
default:
2392-
llvm_unreachable("Unsupported argument in asm.");
2393-
break;
23942352
}
23952353
} else {
23962354
insnTemplate << fmt << "<<" << (mode == Mode_Input ? "in" : "out")
@@ -2401,13 +2359,6 @@ struct AsmProcessor {
24012359
void addOperand2(const char *fmtpre, const char *fmtpost, AsmArgType type,
24022360
Expression *e, AsmCode *asmcode,
24032361
AsmArgMode mode = Mode_Input) {
2404-
if (sc->func->isNaked()) {
2405-
// taken from above
2406-
error(stmt->loc, "only global variables can be referenced by identifier in "
2407-
"naked asm");
2408-
return;
2409-
}
2410-
24112362
insnTemplate << fmtpre << "<<" << (mode == Mode_Input ? "in" : "out")
24122363
<< ">>" << fmtpost;
24132364
asmcode->args.push_back(AsmArg(type, e, mode));
@@ -3078,11 +3029,9 @@ struct AsmProcessor {
30783029
// If we subtract 8 from our const-displacement, then we can use the `H` modifier to ensure
30793030
// that we always end up with a valid syntax for a memory operand with an offset.
30803031
// So, we do just that when we have const-displacement in play.
3081-
// (Only for non-naked asm, as this isn't an issue for naked asm.)
30823032
//
30833033
// See also: https://lists.llvm.org/pipermail/llvm-dev/2017-August/116244.html
3084-
const auto forceLeadingDisplacement = hasConstDisplacement && !sc->func->isNaked();
3085-
if (forceLeadingDisplacement) {
3034+
if (hasConstDisplacement) {
30863035
// Subtract 8 from our const-displacement, and prepare to add the 8 from the `H` modifier.
30873036
insnTemplate << "-8+";
30883037
}
@@ -3092,14 +3041,11 @@ struct AsmProcessor {
30923041
use_star = false;
30933042
}
30943043

3095-
if (!sc->func->isNaked()) // no addrexp in naked asm please :)
3096-
{
3097-
Type *tt = pointerTo(e->type);
3098-
e = createAddrExp(Loc(), e);
3099-
e->type = tt;
3100-
}
3044+
Type *tt = pointerTo(e->type);
3045+
e = createAddrExp(Loc(), e);
3046+
e->type = tt;
31013047

3102-
if (forceLeadingDisplacement) {
3048+
if (hasConstDisplacement) {
31033049
// We have a const-displacement in play, so we add the `H` modifier, as described earlier.
31043050
insnTemplate << "${" << "<<" << (mode == Mode_Input ? "in" : "out")
31053051
<< asmcode->args.size() << ">>" << ":H}";

gen/asmstmt.cpp

Lines changed: 8 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -532,9 +532,14 @@ void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p) {
532532
}
533533
Identifier *const ident = targetLabel->ident;
534534

535-
// if internal, no special handling is necessary, skip
536-
if (llvm::any_of(asmblock->internalLabels,
537-
[ident](Identifier *i) { return i->equals(ident); })) {
535+
// if the label is defined in inline asm, we can jump to it directly
536+
if (fd->isNaked() // in naked DMD-style inline-asm functions, all D labels
537+
// become assembly labels too (and the extra forwarding
538+
// code would mess with the stack due to an i32 alloca)
539+
|| (fd->asmLabels &&
540+
llvm::any_of(*fd->asmLabels, [ident](Identifier *i) {
541+
return i->equals(ident);
542+
}))) {
538543
continue;
539544
}
540545

@@ -784,21 +789,3 @@ void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p) {
784789
p->ir->SetInsertPoint(bb);
785790
}
786791
}
787-
788-
//////////////////////////////////////////////////////////////////////////////
789-
790-
void AsmStatement_toNakedIR(InlineAsmStatement *stmt, IRState *irs) {
791-
IF_LOG Logger::println("InlineAsmStatement::toNakedIR(): %s",
792-
stmt->loc.toChars());
793-
LOG_SCOPE;
794-
795-
// is there code?
796-
if (!stmt->asmcode) {
797-
return;
798-
}
799-
AsmCode *code = static_cast<AsmCode *>(stmt->asmcode);
800-
801-
// build asm stmt
802-
replace_func_name(irs, code->insnTemplate);
803-
irs->nakedAsm << "\t" << code->insnTemplate << std::endl;
804-
}

gen/functions.cpp

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -773,10 +773,8 @@ static LinkageWithCOMDAT lowerFuncLinkage(FuncDeclaration *fdecl) {
773773
return LinkageWithCOMDAT(LLGlobalValue::ExternalLinkage, false);
774774
}
775775

776-
// A body-less declaration always needs to be marked as external in LLVM
777-
// (also e.g. naked template functions which would otherwise be weak_odr,
778-
// but where the definition is in module-level inline asm).
779-
if (!fdecl->fbody || fdecl->isNaked()) {
776+
// A body-less declaration always needs to be marked as external in LLVM.
777+
if (!fdecl->fbody) {
780778
return LinkageWithCOMDAT(LLGlobalValue::ExternalLinkage, false);
781779
}
782780

@@ -1119,12 +1117,6 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
11191117
gIR->funcGenStates.pop_back();
11201118
};
11211119

1122-
// if this function is naked, we take over right away! no standard processing!
1123-
if (fd->isNaked()) {
1124-
DtoDefineNakedFunction(fd);
1125-
return;
1126-
}
1127-
11281120
SCOPE_EXIT {
11291121
if (irFunc->isDynamicCompiled()) {
11301122
defineDynamicCompiledFunction(gIR, irFunc);
@@ -1208,31 +1200,26 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
12081200
gIR->ir->setFastMathFlags(irFunc->FMF);
12091201
gIR->DBuilder.EmitFuncStart(fd);
12101202

1211-
// @naked: emit body and return, no prologue/epilogue
1212-
if (func->hasFnAttribute(llvm::Attribute::Naked)) {
1213-
Statement_toIR(fd->fbody, gIR);
1214-
const bool wasDummy = eraseDummyAfterReturnBB(gIR->scopebb());
1215-
if (!wasDummy && !gIR->scopereturned()) {
1216-
// this is what clang does to prevent LLVM complaining about
1217-
// non-terminated function
1218-
gIR->ir->CreateUnreachable();
1219-
}
1220-
return;
1203+
if (fd->isNaked()) { // DMD-style `asm { naked; }`
1204+
func->addFnAttr(llvm::Attribute::Naked);
12211205
}
12221206

1207+
// @naked: emit body and return, no prologue/epilogue
1208+
const bool isNaked = func->hasFnAttribute(llvm::Attribute::Naked);
1209+
12231210
// create temporary allocas block
12241211
funcGen.allocasBlock =
12251212
llvm::BasicBlock::Create(gIR->context(), "allocas", func);
12261213

12271214
emitInstrumentationFnEnter(fd);
12281215

12291216
if (global.params.trace && fd->emitInstrumentation && !fd->isCMain() &&
1230-
!fd->isNaked()) {
1217+
!isNaked) {
12311218
emitDMDStyleFunctionTrace(*gIR, fd, funcGen);
12321219
}
12331220

12341221
// give the 'this' parameter (an lvalue) storage and debug info
1235-
if (irFty.arg_this) {
1222+
if (!isNaked && irFty.arg_this) {
12361223
LLValue *thisvar = irFunc->thisArg;
12371224
assert(thisvar);
12381225

@@ -1254,7 +1241,7 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
12541241
}
12551242

12561243
// define all explicit parameters
1257-
if (fd->parameters)
1244+
if (!isNaked && fd->parameters)
12581245
defineParameters(irFty, *fd->parameters);
12591246

12601247
// Initialize PGO state for this function
@@ -1320,7 +1307,10 @@ void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally) {
13201307

13211308
// pass the previous block into this block
13221309
gIR->DBuilder.EmitStopPoint(fd->endloc);
1323-
if (func->getReturnType() == LLType::getVoidTy(gIR->context())) {
1310+
1311+
if (isNaked) {
1312+
gIR->ir->CreateUnreachable();
1313+
} else if (func->getReturnType() == LLType::getVoidTy(gIR->context())) {
13241314
gIR->ir->CreateRetVoid();
13251315
} else if (isAnyMainFunction(fd)) {
13261316
gIR->ir->CreateRet(LLConstant::getNullValue(func->getReturnType()));

gen/functions.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,6 @@ void DtoResolveFunction(FuncDeclaration *fdecl);
4040
void DtoDeclareFunction(FuncDeclaration *fdecl);
4141
void DtoDefineFunction(FuncDeclaration *fd, bool linkageAvailableExternally = false);
4242

43-
void DtoDefineNakedFunction(FuncDeclaration *fd);
4443
void emitABIReturnAsmStmt(IRAsmBlock *asmblock, Loc loc,
4544
FuncDeclaration *fdecl);
4645

gen/irstate.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,6 @@ struct IRAsmBlock {
9494
std::set<std::string> clobs;
9595
size_t outputcount;
9696

97-
// stores the labels within the asm block
98-
std::vector<Identifier *> internalLabels;
99-
10097
CompoundAsmStatement *asmBlock;
10198
LLType *retty;
10299
unsigned retn;
@@ -220,7 +217,6 @@ struct IRState {
220217

221218
// for inline asm
222219
IRAsmBlock *asmBlock = nullptr;
223-
std::ostringstream nakedAsm;
224220

225221
// Globals to pin in the llvm.used array to make sure they are not
226222
// eliminated.

0 commit comments

Comments
 (0)