Skip to content

Commit

Permalink
Fix Windows codegen (LLVM globals) in non-singlemodule mode (crystal-…
Browse files Browse the repository at this point in the history
…lang#8978)

Windows compilation so far was always being done in --single-module mode because --cross-compile implies it. But if one adds enough workarounds to try out the compiler *on* Windows, they would run into these problems, unless --single-module is specified.

* Problem 1:

  `__CxxFrameHandler3` is being added only in the main module but can be used in any other module, and the attempt would cause an error:

      Undefined llvm function: __CxxFrameHandler3

  Instead, define this function once per module (still on-demand).

* Problem 2:

  `void_ptr_type_descriptor` is being defined as an LLVM global once per module. But because it's the exact same symbol in all the modules, linking them together leads to this error (repeated per module):

      I-O-5858F-ileD-escriptor43.o : error LNK2005: "void *`RTTI Type Descriptor'" (??_R0PEAX@8) already defined in _main.o.

  Instead, define it only in the main module (still on-demand) and then let other modules refer to it as an external constant.
  • Loading branch information
oprypin authored and carlhoerberg committed Apr 29, 2020
1 parent d500cc0 commit 5e66716
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 20 deletions.
4 changes: 1 addition & 3 deletions src/compiler/crystal/codegen/codegen.cr
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,7 @@ module Crystal

if @program.has_flag? "windows"
@personality_name = "__CxxFrameHandler3"

personality_function = @llvm_mod.functions.add(@personality_name, [] of LLVM::Type, llvm_context.int32, true)
@main.personality_function = personality_function
@main.personality_function = windows_personality_fun
else
@personality_name = "__crystal_personality"
end
Expand Down
8 changes: 7 additions & 1 deletion src/compiler/crystal/codegen/exception.cr
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class Crystal::CodeGenVisitor

windows = @program.has_flag? "windows"

context.fun.personality_function = @llvm_mod.functions[@personality_name] if windows
context.fun.personality_function = windows_personality_fun if windows

# This is the block which is entered when the body raises an exception
rescue_block = new_block "rescue"
Expand Down Expand Up @@ -314,4 +314,10 @@ class Crystal::CodeGenVisitor
@llvm_mod.functions.add("_CxxThrowException", [llvm_context.void_pointer, llvm_context.void_pointer], llvm_context.void, false)
end
end

private def windows_personality_fun
@llvm_mod.functions["__CxxFrameHandler3"]? || begin
@llvm_mod.functions.add("__CxxFrameHandler3", [] of LLVM::Type, llvm_context.int32, true)
end
end
end
45 changes: 29 additions & 16 deletions src/compiler/crystal/codegen/primitives.cr
Original file line number Diff line number Diff line change
Expand Up @@ -1278,32 +1278,45 @@ class Crystal::CodeGenVisitor
def void_ptr_type_descriptor
void_ptr_type_descriptor_name = "\u{1}??_R0PEAX@8"

@llvm_mod.globals[void_ptr_type_descriptor_name]? || begin
if existing = @llvm_mod.globals[void_ptr_type_descriptor_name]?
return existing
end

type_descriptor = llvm_context.struct([
llvm_context.void_pointer.pointer,
llvm_context.void_pointer,
llvm_context.int8.array(6),
])

if !@main_mod.globals[void_ptr_type_descriptor_name]?
base_type_descriptor = external_constant(llvm_context.void_pointer, "\u{1}??_7type_info@@6B@")

# .PEAX is void*
void_ptr_type_descriptor = @llvm_mod.globals.add(
llvm_context.struct([
llvm_context.void_pointer.pointer,
llvm_context.void_pointer,
llvm_context.int8.array(6),
]), void_ptr_type_descriptor_name)
void_ptr_type_descriptor = @main_mod.globals.add(
type_descriptor, void_ptr_type_descriptor_name)
void_ptr_type_descriptor.initializer = llvm_context.const_struct [
base_type_descriptor,
llvm_context.void_pointer.null,
llvm_context.const_string(".PEAX"),
]

void_ptr_type_descriptor
end

# if @llvm_mod == @main_mod, this will find the previously created void_ptr_type_descriptor
external_constant(type_descriptor, void_ptr_type_descriptor_name)
end

def void_ptr_throwinfo
void_ptr_throwinfo_name = "_TI1PEAX"

@llvm_mod.globals[void_ptr_throwinfo_name]? || begin
if existing = @llvm_mod.globals[void_ptr_throwinfo_name]?
return existing
end

eh_throwinfo = llvm_context.struct([llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32])

if !@main_mod.globals[void_ptr_throwinfo_name]?
catchable_type = llvm_context.struct([llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32])
void_ptr_catchable_type = @llvm_mod.globals.add(
void_ptr_catchable_type = @main_mod.globals.add(
catchable_type, "_CT??_R0PEAX@88")
void_ptr_catchable_type.initializer = llvm_context.const_struct [
int32(1),
Expand All @@ -1316,25 +1329,25 @@ class Crystal::CodeGenVisitor
]

catchable_type_array = llvm_context.struct([llvm_context.int32, llvm_context.int32.array(1)])
catchable_void_ptr = @llvm_mod.globals.add(
catchable_void_ptr = @main_mod.globals.add(
catchable_type_array, "_CTA1PEAX")
catchable_void_ptr.initializer = llvm_context.const_struct [
int32(1),
llvm_context.int32.const_array([sub_image_base(void_ptr_catchable_type)]),
]

eh_throwinfo = llvm_context.struct([llvm_context.int32, llvm_context.int32, llvm_context.int32, llvm_context.int32])
void_ptr_throwinfo = @llvm_mod.globals.add(
void_ptr_throwinfo = @main_mod.globals.add(
eh_throwinfo, void_ptr_throwinfo_name)
void_ptr_throwinfo.initializer = llvm_context.const_struct [
int32(0),
int32(0),
int32(0),
sub_image_base(catchable_void_ptr),
]

void_ptr_throwinfo
end

# if @llvm_mod == @main_mod, this will find the previously created void_ptr_throwinfo
external_constant(eh_throwinfo, void_ptr_throwinfo_name)
end

def external_constant(type, name)
Expand Down

0 comments on commit 5e66716

Please sign in to comment.