Skip to content

Instantly share code, notes, and snippets.

@ysbaddaden
Created February 5, 2025 18:02
Show Gist options
  • Save ysbaddaden/f497c0480d9f2dee5f175181d4ca0511 to your computer and use it in GitHub Desktop.
Save ysbaddaden/f497c0480d9f2dee5f175181d4ca0511 to your computer and use it in GitHub Desktop.
diff --git a/src/compiler/crystal/codegen/ast.cr b/src/compiler/crystal/codegen/ast.cr
index ab4fa76d4..ed3465be1 100644
--- a/src/compiler/crystal/codegen/ast.cr
+++ b/src/compiler/crystal/codegen/ast.cr
@@ -72,6 +72,10 @@ module Crystal
nil
end
+ def linkage
+ nil
+ end
+
@c_calling_convention : Bool? = nil
property c_calling_convention
diff --git a/src/compiler/crystal/codegen/codegen.cr b/src/compiler/crystal/codegen/codegen.cr
index 7e15b1bdc..e4d05ba4a 100644
--- a/src/compiler/crystal/codegen/codegen.cr
+++ b/src/compiler/crystal/codegen/codegen.cr
@@ -283,6 +283,7 @@ module Crystal
ret_type = @llvm_typer.llvm_return_type(@main_ret_type)
main_type = LLVM::Type.function([llvm_context.int32, llvm_context.void_pointer.pointer], ret_type)
@main = @llvm_mod.functions.add(MAIN_NAME, main_type)
+ @main.linkage = LLVM::Linkage::Internal if @single_module
@fun_types = { {@llvm_mod, MAIN_NAME} => main_type }
if @program.has_flag?("msvc")
diff --git a/src/compiler/crystal/codegen/fun.cr b/src/compiler/crystal/codegen/fun.cr
index c56bde6e5..69467027d 100644
--- a/src/compiler/crystal/codegen/fun.cr
+++ b/src/compiler/crystal/codegen/fun.cr
@@ -337,7 +337,7 @@ class Crystal::CodeGenVisitor
end
end
- if @single_module && !target_def.no_inline? && !target_def.is_a?(External)
+ if @single_module && !target_def.is_a?(External)
context.fun.linkage = LLVM::Linkage::Internal
end
@@ -395,6 +395,10 @@ class Crystal::CodeGenVisitor
context.fun.call_convention = call_convention
end
+ if @single_module && (linkage = target_def.linkage)
+ context.fun.linkage = linkage
+ end
+
i = 0
args.each do |arg|
param = context.fun.params[i + offset]
@@ -448,11 +452,7 @@ class Crystal::CodeGenVisitor
context.fun.add_attribute LLVM::Attribute::ReturnsTwice if target_def.returns_twice?
context.fun.add_attribute LLVM::Attribute::Naked if target_def.naked?
context.fun.add_attribute LLVM::Attribute::NoReturn if target_def.no_returns?
-
- if target_def.no_inline?
- context.fun.add_attribute LLVM::Attribute::NoInline
- context.fun.linkage = LLVM::Linkage::External
- end
+ context.fun.add_attribute LLVM::Attribute::NoInline if target_def.no_inline?
end
def setup_closure_vars(def_vars, closure_vars, context, closure_type, closure_ptr)
diff --git a/src/compiler/crystal/program.cr b/src/compiler/crystal/program.cr
index 840afd2b6..699da7fe4 100644
--- a/src/compiler/crystal/program.cr
+++ b/src/compiler/crystal/program.cr
@@ -239,6 +239,7 @@ module Crystal
types["Extern"] = @extern_annotation = AnnotationType.new self, self, "Extern"
types["Flags"] = @flags_annotation = AnnotationType.new self, self, "Flags"
types["Link"] = @link_annotation = AnnotationType.new self, self, "Link"
+ types["Linkage"] = @linkage_annotation = AnnotationType.new self, self, "Linkage"
types["Naked"] = @naked_annotation = AnnotationType.new self, self, "Naked"
types["NoInline"] = @no_inline_annotation = AnnotationType.new self, self, "NoInline"
types["Packed"] = @packed_annotation = AnnotationType.new self, self, "Packed"
@@ -534,7 +535,8 @@ module Crystal
packed_annotation thread_local_annotation no_inline_annotation
always_inline_annotation naked_annotation returns_twice_annotation
raises_annotation primitive_annotation call_convention_annotation
- flags_annotation link_annotation extern_annotation deprecated_annotation experimental_annotation) %}
+ flags_annotation link_annotation linkage_annotation extern_annotation
+ deprecated_annotation experimental_annotation) %}
def {{name.id}}
@{{name.id}}.not_nil!
end
diff --git a/src/compiler/crystal/semantic/ast.cr b/src/compiler/crystal/semantic/ast.cr
index f4fa683ef..b2849fbef 100644
--- a/src/compiler/crystal/semantic/ast.cr
+++ b/src/compiler/crystal/semantic/ast.cr
@@ -689,6 +689,7 @@ module Crystal
property real_name : String
property! fun_def : FunDef
property call_convention : LLVM::CallConvention?
+ property linkage : LLVM::Linkage?
property wasm_import_module : String?
property? dead = false
diff --git a/src/compiler/crystal/semantic/top_level_visitor.cr b/src/compiler/crystal/semantic/top_level_visitor.cr
index cfc8dddc8..8c93c52a9 100644
--- a/src/compiler/crystal/semantic/top_level_visitor.cr
+++ b/src/compiler/crystal/semantic/top_level_visitor.cr
@@ -528,6 +528,24 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor
node.body = primitive
end
+ private def process_def_linkage_annotation(node, ann)
+ if ann.args.size != 1
+ ann.wrong_number_of_arguments "annotation Linkage", ann.args.size, 1
+ end
+
+ arg = ann.args.first
+ unless arg.is_a?(StringLiteral)
+ arg.raise "argument to Linkage must be a string"
+ end
+
+ value = LLVM::Linkage.parse?(arg.value)
+ unless value
+ arg.raise "invalid linkage. Valid values are #{LLVM::Linkage.values.join ", "}"
+ end
+
+ node.linkage = value
+ end
+
def visit(node : Include)
check_outside_exp node, "include"
include_in current_type, node, :included
@@ -984,8 +1002,10 @@ class Crystal::TopLevelVisitor < Crystal::SemanticVisitor
call_convention = parse_call_convention(ann, call_convention)
elsif annotation_type == @program.primitive_annotation
process_def_primitive_annotation(external, ann)
+ elsif annotation_type == @program.linkage_annotation
+ process_def_linkage_annotation(external, ann)
else
- ann.raise "funs can only be annotated with: NoInline, AlwaysInline, Naked, ReturnsTwice, Raises, CallConvention"
+ ann.raise "funs can only be annotated with: NoInline, AlwaysInline, Naked, ReturnsTwice, Raises, CallConvention or Linkage"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment