diff --git a/gen/passes/DLLImportRelocation.cpp b/gen/passes/DLLImportRelocation.cpp index b01bed1848d..bc9cfb3f15e 100644 --- a/gen/passes/DLLImportRelocation.cpp +++ b/gen/passes/DLLImportRelocation.cpp @@ -69,17 +69,16 @@ struct Impl { if (!initializer) return false; - // set i to the initializer, skipping over an optional cast - auto i = initializer; - if (auto ce = dyn_cast(i)) { - if (ce->isCast()) - i = ce->getOperand(0); + // set i to the initializer, descending into GEPs and skipping over casts + auto i = skipOverCast(initializer); + if (auto gep = isGEP(i)) { + i = skipOverCast(gep->getOperand(0)); } // check if i is a reference to a dllimport global if (auto globalRef = dyn_cast(i)) { if (globalRef->hasDLLImportStorageClass()) { - onDLLImportReference(globalRef); + onDLLImportReference(globalRef, initializer); return true; } return false; @@ -105,12 +104,13 @@ struct Impl { } private: - void onDLLImportReference(GlobalVariable *importedVar) { + void onDLLImportReference(GlobalVariable *importedVar, + Constant *originalInitializer) { ++NumRelocations; // initialize at runtime: currentGlobal->setConstant(false); - appendToCRTConstructor(importedVar); + appendToCRTConstructor(importedVar, originalInitializer); const auto pathLength = currentGepPath.size(); if (pathLength == 0) { @@ -176,7 +176,8 @@ struct Impl { llvm::appendToGlobalCtors(m, ctor, 0); } - void appendToCRTConstructor(GlobalVariable *importedVar) { + void appendToCRTConstructor(GlobalVariable *importedVar, + Constant *originalInitializer) { if (!ctor) createCRTConstructor(); @@ -197,10 +198,14 @@ struct Impl { } } - Value *value = importedVar; + Constant *value = importedVar; auto t = address->getType()->getPointerElementType(); - if (value->getType() != t) - value = b.CreatePointerCast(value, t); + if (auto gep = isGEP(skipOverCast(originalInitializer))) { + Constant *newOperand = + createConstPointerCast(importedVar, gep->getOperand(0)->getType()); + value = gep->getWithOperandReplaced(0, newOperand); + } + value = createConstPointerCast(value, t); // Only modify the field if the current value is still null from the static // initializer. This is important for multiple definitions of a (templated) @@ -224,6 +229,28 @@ struct Impl { b.CreateStore(value, address); b.CreateBr(endbb); } + + static Constant *skipOverCast(Constant *value) { + if (auto ce = dyn_cast(value)) { + if (ce->isCast()) + return ce->getOperand(0); + } + return value; + } + + static ConstantExpr *isGEP(Constant *value) { + if (auto ce = dyn_cast(value)) { + if (ce->getOpcode() == Instruction::GetElementPtr) + return ce; + } + return nullptr; + } + + static Constant *createConstPointerCast(Constant *value, Type *type) { + return value->getType() == type + ? value + : llvm::ConstantExpr::getPointerCast(value, type); + } }; } diff --git a/tests/codegen/dllimport_gh3926.d b/tests/codegen/dllimport_gh3926.d new file mode 100644 index 00000000000..3c61687d72a --- /dev/null +++ b/tests/codegen/dllimport_gh3926.d @@ -0,0 +1,32 @@ +// REQUIRES: Windows + +// RUN: %ldc -output-ll -dllimport=all -of=%t_all.ll %s && FileCheck %s < %t_all.ll +// RUN: %ldc -output-ll -dllimport=defaultLibsOnly -of=%t_dlo.ll %s && FileCheck %s < %t_dlo.ll + +import std.variant : Variant; // pre-instantiated template + +void foo() +{ + // define TypeInfo_Struct referencing dllimported Variant vtable and init symbol + auto t = typeid(Variant); +} + +// no direct init symbol refs: +// CHECK-NOT: @_D3std7variant__T8VariantN{{.*}}6__initZ + +// dllimported init symbol: +// CHECK: @_D3std7variant__T8VariantN{{.*}}6__initZ = external dllimport + +// no direct init symbol refs: +// CHECK-NOT: @_D3std7variant__T8VariantN{{.*}}6__initZ + +// check generated CRT constructor: +// CHECK: define private void @ldc.dllimport_relocation() + +// should set the vptr: +// CHECK: if: +// CHECK-NEXT: store [{{[0-9]+}} x i8*]* @_D15TypeInfo_Struct6__vtblZ, + +// should set m_init.ptr: +// CHECK: if1: +// CHECK-NEXT: store i8* getelementptr inbounds ({{.*}} @_D3std7variant__T8VariantN{{.*}}6__initZ, i32 0, i32 0, i32 0),