From d5d2e3f51d58075c32214eec49da71d97c154f67 Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Thu, 14 Dec 2023 00:55:11 +0800 Subject: [PATCH 1/2] Support `alignof` and `instance_alignof` --- spec/compiler/codegen/sizeof_spec.cr | 101 ++++++++++++++++++ spec/compiler/formatter/formatter_spec.cr | 2 + spec/compiler/interpreter/sizeof_spec.cr | 18 ++++ spec/compiler/parser/parser_spec.cr | 6 ++ spec/compiler/semantic/sizeof_spec.cr | 26 ++--- .../syntax_highlighter/colorize_spec.cr | 4 +- .../crystal/syntax_highlighter/html_spec.cr | 4 +- src/compiler/crystal/codegen/codegen.cr | 26 ++++- .../crystal/codegen/llvm_builder_helper.cr | 8 ++ src/compiler/crystal/codegen/llvm_typer.cr | 6 +- src/compiler/crystal/interpreter/compiler.cr | 21 +++- src/compiler/crystal/interpreter/context.cr | 24 +++++ src/compiler/crystal/macros.cr | 32 ++++++ src/compiler/crystal/macros/types.cr | 2 + src/compiler/crystal/semantic/ast.cr | 2 +- src/compiler/crystal/semantic/bindings.cr | 6 ++ .../crystal/semantic/cleanup_transformer.cr | 17 +++ src/compiler/crystal/semantic/main_visitor.cr | 53 +++++++-- .../crystal/semantic/type_guess_visitor.cr | 10 +- src/compiler/crystal/semantic/type_lookup.cr | 2 +- src/compiler/crystal/syntax/ast.cr | 12 +++ src/compiler/crystal/syntax/lexer.cr | 30 +++++- src/compiler/crystal/syntax/parser.cr | 26 ++++- src/compiler/crystal/syntax/to_s.cr | 14 +++ src/compiler/crystal/syntax/token.cr | 2 + src/compiler/crystal/syntax/transformer.cr | 32 +----- src/compiler/crystal/tools/formatter.cr | 8 ++ src/llvm/lib_llvm/core.cr | 1 + src/llvm/type.cr | 9 ++ 29 files changed, 431 insertions(+), 73 deletions(-) diff --git a/spec/compiler/codegen/sizeof_spec.cr b/spec/compiler/codegen/sizeof_spec.cr index e3a0690cf06d..8139db8bc426 100644 --- a/spec/compiler/codegen/sizeof_spec.cr +++ b/spec/compiler/codegen/sizeof_spec.cr @@ -243,4 +243,105 @@ describe "Code gen: sizeof" do z)).to_i.should eq(16) end + + describe "alignof" do + it "gets alignof primitive types" do + run("alignof(Int32)").to_i.should eq(4) + run("alignof(Void)").to_i.should eq(1) + run("alignof(NoReturn)").to_i.should eq(1) + run("alignof(Nil)").to_i.should eq(1) + run("alignof(Bool)").to_i.should eq(1) + end + + it "gets alignof struct" do + run(<<-CRYSTAL).to_i.should eq(4) + struct Foo + def initialize(@x : Int8, @y : Int32, @z : Int16) + end + end + + Foo.new(1, 2, 3) + + alignof(Foo) + CRYSTAL + end + + it "gets alignof class" do + # pointer size and alignment should be identical + run(<<-CRYSTAL).to_i.should eq(sizeof(Void*)) + class Foo + def initialize(@x : Int8, @y : Int32, @z : Int16) + end + end + + Foo.new(1, 2, 3) + + alignof(Foo) + CRYSTAL + end + end + + describe "instance_alignof" do + it "gets instance_alignof class" do + run(<<-CRYSTAL).to_i.should eq(4) + class Foo + def initialize(@x : Int8, @y : Int32, @z : Int16) + end + end + + Foo.new(1, 2, 3) + + instance_alignof(Foo) + CRYSTAL + + run(<<-CRYSTAL).to_i.should eq(8) + class Foo + def initialize(@x : Int8, @y : Int64, @z : Int16) + end + end + + Foo.new(1, 2, 3) + + instance_alignof(Foo) + CRYSTAL + + run(<<-CRYSTAL).to_i.should eq(4) + class Foo + end + + Foo.new + + instance_alignof(Foo) + CRYSTAL + end + + it "gets instance_alignof a generic type with type vars" do + run(<<-CRYSTAL).to_i.should eq(4) + class Foo(T) + def initialize(@x : T) + end + end + + instance_alignof(Foo(Int32)) + CRYSTAL + + run(<<-CRYSTAL).to_i.should eq(8) + class Foo(T) + def initialize(@x : T) + end + end + + instance_alignof(Foo(Int64)) + CRYSTAL + + run(<<-CRYSTAL).to_i.should eq(4) + class Foo(T) + def initialize(@x : T) + end + end + + instance_alignof(Foo(Int8)) + CRYSTAL + end + end end diff --git a/spec/compiler/formatter/formatter_spec.cr b/spec/compiler/formatter/formatter_spec.cr index 4cc2ce230550..4567638f0fd8 100644 --- a/spec/compiler/formatter/formatter_spec.cr +++ b/spec/compiler/formatter/formatter_spec.cr @@ -1385,6 +1385,8 @@ describe Crystal::Formatter do assert_format "typeof( 1, 2, 3 )", "typeof(1, 2, 3)" assert_format "sizeof( Int32 )", "sizeof(Int32)" assert_format "instance_sizeof( Int32 )", "instance_sizeof(Int32)" + assert_format "alignof( Int32 )", "alignof(Int32)" + assert_format "instance_alignof( Int32 )", "instance_alignof(Int32)" assert_format "offsetof( String, @length )", "offsetof(String, @length)" assert_format "pointerof( @a )", "pointerof(@a)" diff --git a/spec/compiler/interpreter/sizeof_spec.cr b/spec/compiler/interpreter/sizeof_spec.cr index 4c52247b9687..306bb1bdd199 100644 --- a/spec/compiler/interpreter/sizeof_spec.cr +++ b/spec/compiler/interpreter/sizeof_spec.cr @@ -19,4 +19,22 @@ describe Crystal::Repl::Interpreter do CRYSTAL end end + + context "alignof" do + it "interprets alignof typeof" do + interpret("alignof(typeof(1))").should eq(4) + end + end + + context "instance_alignof" do + it "interprets instance_alignof typeof" do + interpret(<<-CRYSTAL).should eq(8) + class Foo + @x = 0_i64 + end + + instance_alignof(typeof(Foo.new)) + CRYSTAL + end + end end diff --git a/spec/compiler/parser/parser_spec.cr b/spec/compiler/parser/parser_spec.cr index 0ee3eedbdd62..41fad5cc3a89 100644 --- a/spec/compiler/parser/parser_spec.cr +++ b/spec/compiler/parser/parser_spec.cr @@ -768,6 +768,8 @@ module Crystal it_parses "Foo(X, sizeof(Int32))", Generic.new("Foo".path, ["X".path, SizeOf.new("Int32".path)] of ASTNode) it_parses "Foo(X, instance_sizeof(Int32))", Generic.new("Foo".path, ["X".path, InstanceSizeOf.new("Int32".path)] of ASTNode) + it_parses "Foo(X, alignof(Int32))", Generic.new("Foo".path, ["X".path, AlignOf.new("Int32".path)] of ASTNode) + it_parses "Foo(X, instance_alignof(Int32))", Generic.new("Foo".path, ["X".path, InstanceAlignOf.new("Int32".path)] of ASTNode) it_parses "Foo(X, offsetof(Foo, @a))", Generic.new("Foo".path, ["X".path, OffsetOf.new("Foo".path, "@a".instance_var)] of ASTNode) it_parses "Foo(\n)", Generic.new("Foo".path, [] of ASTNode) @@ -1213,6 +1215,8 @@ module Crystal it_parses "sizeof(X)", SizeOf.new("X".path) it_parses "instance_sizeof(X)", InstanceSizeOf.new("X".path) + it_parses "alignof(X)", AlignOf.new("X".path) + it_parses "instance_alignof(X)", InstanceAlignOf.new("X".path) it_parses "offsetof(X, @a)", OffsetOf.new("X".path, "@a".instance_var) it_parses "offsetof(X, 1)", OffsetOf.new("X".path, 1.int32) assert_syntax_error "offsetof(X, 1.0)", "expecting an integer offset, not '1.0'" @@ -1254,6 +1258,8 @@ module Crystal # multiline pseudo methods (#8318) it_parses "sizeof(\n Int32\n)", SizeOf.new(Path.new("Int32")) it_parses "instance_sizeof(\n Int32\n)", InstanceSizeOf.new(Path.new("Int32")) + it_parses "alignof(\n Int32\n)", AlignOf.new(Path.new("Int32")) + it_parses "instance_alignof(\n Int32\n)", InstanceAlignOf.new(Path.new("Int32")) it_parses "typeof(\n 1\n)", TypeOf.new([1.int32] of ASTNode) it_parses "offsetof(\n Foo,\n @foo\n)", OffsetOf.new(Path.new("Foo"), InstanceVar.new("@foo")) it_parses "pointerof(\n foo\n)", PointerOf.new("foo".call) diff --git a/spec/compiler/semantic/sizeof_spec.cr b/spec/compiler/semantic/sizeof_spec.cr index 1cdab6a6ed3b..4b24069ebcb5 100644 --- a/spec/compiler/semantic/sizeof_spec.cr +++ b/spec/compiler/semantic/sizeof_spec.cr @@ -1,24 +1,18 @@ require "../../spec_helper" describe "Semantic: sizeof" do - it "types sizeof" do - assert_type("sizeof(Float64)") { int32 } - end - - it "types sizeof NoReturn (missing type) (#5717)" do - assert_type("x = nil; x ? sizeof(typeof(x)) : 1") { int32 } - end + {% for name in %w(sizeof instance_sizeof alignof instance_alignof).map(&.id) %} + it "types {{name}}" do + assert_type("{{name}}(Reference)") { int32 } + end - it "types instance_sizeof" do - assert_type("instance_sizeof(Reference)") { int32 } - end - - it "types instance_sizeof NoReturn (missing type) (#5717)" do - assert_type("x = nil; x ? instance_sizeof(typeof(x)) : 1") { int32 } - end + it "types {{name}} NoReturn (missing type) (#5717)" do + assert_type("x = nil; x ? {{name}}(typeof(x)) : 1") { int32 } + end + {% end %} it "errors on sizeof uninstantiated generic type (#6415)" do - assert_error "sizeof(Array)", "can't take sizeof uninstantiated generic type Array(T)" + assert_error "sizeof(Array)", "can't take size of uninstantiated generic type Array(T)" end it "gives error if using instance_sizeof on something that's not a class" do @@ -84,7 +78,7 @@ describe "Semantic: sizeof" do end it "gives error if using instance_sizeof on a generic type without type vars" do - assert_error "instance_sizeof(Array)", "can't take instance_sizeof uninstantiated generic type Array(T)" + assert_error "instance_sizeof(Array)", "can't take instance size of uninstantiated generic type Array(T)" end it "gives error if using instance_sizeof on a union type (#8349)" do diff --git a/spec/std/crystal/syntax_highlighter/colorize_spec.cr b/spec/std/crystal/syntax_highlighter/colorize_spec.cr index c989112264fc..74bc591ac51b 100644 --- a/spec/std/crystal/syntax_highlighter/colorize_spec.cr +++ b/spec/std/crystal/syntax_highlighter/colorize_spec.cr @@ -55,10 +55,10 @@ describe Crystal::SyntaxHighlighter::Colorize do def if else elsif end class module include extend while until do yield return unless next break begin lib fun type struct union enum macro out require - case when select then of rescue ensure is_a? alias sizeof + case when select then of rescue ensure is_a? alias sizeof alignof as as? typeof for in with super private asm nil? abstract pointerof - protected uninitialized instance_sizeof offsetof + protected uninitialized instance_sizeof instance_alignof offsetof annotation verbatim ).each do |kw| it_highlights kw, %(\e[91m#{kw}\e[0m) diff --git a/spec/std/crystal/syntax_highlighter/html_spec.cr b/spec/std/crystal/syntax_highlighter/html_spec.cr index 84e7c69ff410..2d8b37a4f51e 100644 --- a/spec/std/crystal/syntax_highlighter/html_spec.cr +++ b/spec/std/crystal/syntax_highlighter/html_spec.cr @@ -50,10 +50,10 @@ describe Crystal::SyntaxHighlighter::HTML do def if else elsif end class module include extend while until do yield return unless next break begin lib fun type struct union enum macro out require - case when select then of rescue ensure is_a? alias sizeof + case when select then of rescue ensure is_a? alias sizeof alignof as as? typeof for in with self super private asm nil? abstract pointerof - protected uninitialized instance_sizeof offsetof + protected uninitialized instance_sizeof instance_alignof offsetof annotation verbatim ).each do |kw| it_highlights kw, %(#{kw}) diff --git a/src/compiler/crystal/codegen/codegen.cr b/src/compiler/crystal/codegen/codegen.cr index d41b71edd042..da9fc8ea3f6a 100644 --- a/src/compiler/crystal/codegen/codegen.cr +++ b/src/compiler/crystal/codegen/codegen.cr @@ -91,7 +91,7 @@ module Crystal # We need `sizeof(Void)` to be 1 because doing # `Pointer(Void).malloc` must work like `Pointer(UInt8).malloc`, # that is, consider Void like the size of a byte. - 1 + 1_u64 else llvm_typer.size_of(llvm_typer.llvm_type(type)) end @@ -101,6 +101,20 @@ module Crystal llvm_typer.size_of(llvm_typer.llvm_struct_type(type)) end + def align_of(type) + if type.void? + # We need `alignof(Void)` to be 1 because it is effectively the smallest + # possible alignment for any type. + 1_u32 + else + llvm_typer.align_of(llvm_typer.llvm_type(type)) + end + end + + def instance_align_of(type) + llvm_typer.align_of(llvm_typer.llvm_struct_type(type)) + end + def offset_of(type, element_index) return 0_u64 if type.extern_union? || type.is_a?(StaticArrayInstanceType) llvm_typer.offset_of(llvm_typer.llvm_type(type), element_index) @@ -824,6 +838,16 @@ module Crystal false end + def visit(node : AlignOf) + @last = trunc(llvm_alignment(node.exp.type.sizeof_type), llvm_context.int32) + false + end + + def visit(node : InstanceAlignOf) + @last = trunc(llvm_struct_alignment(node.exp.type.sizeof_type), llvm_context.int32) + false + end + def visit(node : Include) node.hook_expansions.try &.each do |hook| accept hook diff --git a/src/compiler/crystal/codegen/llvm_builder_helper.cr b/src/compiler/crystal/codegen/llvm_builder_helper.cr index 088ea772552e..4f2ae5542c38 100644 --- a/src/compiler/crystal/codegen/llvm_builder_helper.cr +++ b/src/compiler/crystal/codegen/llvm_builder_helper.cr @@ -227,5 +227,13 @@ module Crystal def llvm_struct_size(type) llvm_struct_type(type).size end + + def llvm_alignment(type) + llvm_type(type).alignment + end + + def llvm_struct_alignment(type) + llvm_struct_type(type).alignment + end end end diff --git a/src/compiler/crystal/codegen/llvm_typer.cr b/src/compiler/crystal/codegen/llvm_typer.cr index 8b2cfdf65ec4..d20fbd59fa9a 100644 --- a/src/compiler/crystal/codegen/llvm_typer.cr +++ b/src/compiler/crystal/codegen/llvm_typer.cr @@ -574,7 +574,11 @@ module Crystal end def align_of(type) - @layout.abi_alignment(type) + if type.void? + 1_u32 + else + @layout.abi_alignment(type) + end end def size_t diff --git a/src/compiler/crystal/interpreter/compiler.cr b/src/compiler/crystal/interpreter/compiler.cr index 7d56d8121bed..bfd1514b419f 100644 --- a/src/compiler/crystal/interpreter/compiler.cr +++ b/src/compiler/crystal/interpreter/compiler.cr @@ -1430,6 +1430,22 @@ class Crystal::Repl::Compiler < Crystal::Visitor false end + def visit(node : AlignOf) + return false unless @wants_value + + put_i32 inner_alignof_type(node.exp), node: node + + false + end + + def visit(node : InstanceAlignOf) + return false unless @wants_value + + put_i32 inner_instance_alignof_type(node.exp), node: node + + false + end + def visit(node : TypeNode) return false unless @wants_value @@ -3369,8 +3385,9 @@ class Crystal::Repl::Compiler < Crystal::Visitor @instructions.instructions.size end - private delegate inner_sizeof_type, aligned_sizeof_type, - inner_instance_sizeof_type, aligned_instance_sizeof_type, to: @context + private delegate inner_sizeof_type, inner_alignof_type, aligned_sizeof_type, + inner_instance_sizeof_type, inner_instance_alignof_type, aligned_instance_sizeof_type, + to: @context private def ivar_offset(type : Type, name : String) : Int32 if type.extern_union? diff --git a/src/compiler/crystal/interpreter/context.cr b/src/compiler/crystal/interpreter/context.cr index 268d67448125..f4a4444e105c 100644 --- a/src/compiler/crystal/interpreter/context.cr +++ b/src/compiler/crystal/interpreter/context.cr @@ -315,6 +315,18 @@ class Crystal::Repl::Context 0 end + def inner_alignof_type(node : ASTNode) : Int32 + inner_alignof_type(node.type?) + end + + def inner_alignof_type(type : Type) : Int32 + @program.align_of(type.sizeof_type).to_i32 + end + + def inner_alignof_type(type : Nil) : Int32 + 0 + end + def aligned_instance_sizeof_type(type : Type) : Int32 align(inner_instance_sizeof_type(type)) end @@ -331,6 +343,18 @@ class Crystal::Repl::Context 0 end + def inner_instance_alignof_type(node : ASTNode) : Int32 + inner_instance_alignof_type(node.type?) + end + + def inner_instance_alignof_type(type : Type) : Int32 + @program.instance_align_of(type.sizeof_type).to_i32 + end + + def inner_instance_alignof_type(type : Nil) : Int32 + 0 + end + def offset_of(type : Type, index : Int32) : Int32 @program.offset_of(type.sizeof_type, index).to_i32 end diff --git a/src/compiler/crystal/macros.cr b/src/compiler/crystal/macros.cr index 3c59a56a1818..983af69ae4b7 100644 --- a/src/compiler/crystal/macros.cr +++ b/src/compiler/crystal/macros.cr @@ -1452,13 +1452,45 @@ module Crystal::Macros end # A `sizeof` expression. + # + # Every expression `node` is equivalent to: + # + # ``` + # sizeof({{ node.exp }}) + # ``` class SizeOf < UnaryExpression end # An `instance_sizeof` expression. + # + # Every expression `node` is equivalent to: + # + # ``` + # instance_sizeof({{ node.exp }}) + # ``` class InstanceSizeOf < UnaryExpression end + # A `alignof` expression. + # + # Every expression `node` is equivalent to: + # + # ``` + # alignof({{ node.exp }}) + # ``` + class AlignOf < UnaryExpression + end + + # An `instance_alignof` expression. + # + # Every expression `node` is equivalent to: + # + # ``` + # instance_alignof({{ node.exp }}) + # ``` + class InstanceAlignOf < UnaryExpression + end + # An `out` expression. class Out < UnaryExpression end diff --git a/src/compiler/crystal/macros/types.cr b/src/compiler/crystal/macros/types.cr index 73ae695066ba..068f92e3d23a 100644 --- a/src/compiler/crystal/macros/types.cr +++ b/src/compiler/crystal/macros/types.cr @@ -54,6 +54,8 @@ module Crystal @macro_types["PointerOf"] = NonGenericMacroType.new self, "PointerOf", unary_expression @macro_types["SizeOf"] = NonGenericMacroType.new self, "SizeOf", unary_expression @macro_types["InstanceSizeOf"] = NonGenericMacroType.new self, "InstanceSizeOf", unary_expression + @macro_types["AlignOf"] = NonGenericMacroType.new self, "AlignOf", unary_expression + @macro_types["InstanceAlignOf"] = NonGenericMacroType.new self, "InstanceAlignOf", unary_expression @macro_types["Out"] = NonGenericMacroType.new self, "Out", unary_expression @macro_types["Splat"] = NonGenericMacroType.new self, "Splat", unary_expression @macro_types["DoubleSplat"] = NonGenericMacroType.new self, "DoubleSplat", unary_expression diff --git a/src/compiler/crystal/semantic/ast.cr b/src/compiler/crystal/semantic/ast.cr index d976752b5777..f4fa683efe82 100644 --- a/src/compiler/crystal/semantic/ast.cr +++ b/src/compiler/crystal/semantic/ast.cr @@ -653,7 +653,7 @@ module Crystal ArrayLiteral HashLiteral RegexLiteral RangeLiteral Case StringInterpolation MacroExpression MacroIf MacroFor MacroVerbatim MultiAssign - SizeOf InstanceSizeOf OffsetOf Global Require Select) %} + SizeOf InstanceSizeOf AlignOf InstanceAlignOf OffsetOf Global Require Select) %} class {{name.id}} include ExpandableNode end diff --git a/src/compiler/crystal/semantic/bindings.cr b/src/compiler/crystal/semantic/bindings.cr index e2a24f00bdff..c5fe9f164742 100644 --- a/src/compiler/crystal/semantic/bindings.cr +++ b/src/compiler/crystal/semantic/bindings.cr @@ -600,6 +600,12 @@ module Crystal if node.is_a?(InstanceSizeOf) && (expanded = node.expanded) node = expanded end + if node.is_a?(AlignOf) && (expanded = node.expanded) + node = expanded + end + if node.is_a?(InstanceAlignOf) && (expanded = node.expanded) + node = expanded + end if node.is_a?(OffsetOf) && (expanded = node.expanded) node = expanded end diff --git a/src/compiler/crystal/semantic/cleanup_transformer.cr b/src/compiler/crystal/semantic/cleanup_transformer.cr index 70a8ffe63590..c95f31013558 100644 --- a/src/compiler/crystal/semantic/cleanup_transformer.cr +++ b/src/compiler/crystal/semantic/cleanup_transformer.cr @@ -1003,6 +1003,23 @@ module Crystal node end + def transform(node : InstanceAlignOf) + exp_type = node.exp.type? + + if exp_type + instance_type = exp_type.devirtualize + if instance_type.struct? || instance_type.module? || instance_type.metaclass? || instance_type.is_a?(UnionType) + node.exp.raise "instance_alignof can only be used with a class, but #{instance_type} is a #{instance_type.type_desc}" + end + end + + if expanded = node.expanded + return expanded.transform self + end + + node + end + def transform(node : TupleLiteral) super diff --git a/src/compiler/crystal/semantic/main_visitor.cr b/src/compiler/crystal/semantic/main_visitor.cr index 712d9e8d72cf..ebfc630353f7 100644 --- a/src/compiler/crystal/semantic/main_visitor.cr +++ b/src/compiler/crystal/semantic/main_visitor.cr @@ -2589,6 +2589,30 @@ module Crystal end def visit(node : SizeOf) + visit_size_or_align_of(node) do |type| + @program.size_of(type.sizeof_type) + end + end + + def visit(node : InstanceSizeOf) + visit_instance_size_or_align_of(node) do |type| + @program.instance_size_of(type.sizeof_type) + end + end + + def visit(node : AlignOf) + visit_size_or_align_of(node) do |type| + @program.align_of(type.sizeof_type) + end + end + + def visit(node : InstanceAlignOf) + visit_instance_size_or_align_of(node) do |type| + @program.instance_align_of(type.sizeof_type) + end + end + + private def visit_size_or_align_of(node) @in_type_args += 1 node.exp.accept self @in_type_args -= 1 @@ -2596,15 +2620,15 @@ module Crystal type = node.exp.type? if type.is_a?(GenericType) - node.exp.raise "can't take sizeof uninstantiated generic type #{type}" + node.exp.raise "can't take #{sizeof_description(node)} of uninstantiated generic type #{type}" end - # Try to resolve the sizeof right now to a number literal - # (useful for sizeof inside as a generic type argument, but also + # Try to resolve the node right now to a number literal + # (useful for sizeof/alignof inside as a generic type argument, but also # to make it easier for LLVM to optimize things) if type && !node.exp.is_a?(TypeOf) && !(type.module? || (type.abstract? && type.struct?)) - expanded = NumberLiteral.new(@program.size_of(type.sizeof_type).to_s, :i32) + expanded = NumberLiteral.new(yield(type).to_s, :i32) expanded.type = @program.int32 node.expanded = expanded end @@ -2614,7 +2638,7 @@ module Crystal false end - def visit(node : InstanceSizeOf) + private def visit_instance_size_or_align_of(node) @in_type_args += 1 node.exp.accept self @in_type_args -= 1 @@ -2622,14 +2646,14 @@ module Crystal type = node.exp.type? if type.is_a?(GenericType) - node.exp.raise "can't take instance_sizeof uninstantiated generic type #{type}" + node.exp.raise "can't take #{sizeof_description(node)} of uninstantiated generic type #{type}" end # Try to resolve the instance_sizeof right now to a number literal - # (useful for sizeof inside as a generic type argument, but also + # (useful for instance_sizeof inside as a generic type argument, but also # to make it easier for LLVM to optimize things) if type && type.devirtualize.class? && !type.metaclass? && !type.struct? && !node.exp.is_a?(TypeOf) - expanded = NumberLiteral.new(@program.instance_size_of(type.sizeof_type).to_s, :i32) + expanded = NumberLiteral.new(yield(type).to_s, :i32) expanded.type = @program.int32 node.expanded = expanded end @@ -2639,6 +2663,19 @@ module Crystal false end + private def sizeof_description(node) + case node + in SizeOf + "size" + in AlignOf + "alignment" + in InstanceSizeOf + "instance size" + in InstanceAlignOf + "instance alignment" + end + end + def visit(node : OffsetOf) @in_type_args += 1 node.offsetof_type.accept self diff --git a/src/compiler/crystal/semantic/type_guess_visitor.cr b/src/compiler/crystal/semantic/type_guess_visitor.cr index 4eb13804f64d..693cade2906b 100644 --- a/src/compiler/crystal/semantic/type_guess_visitor.cr +++ b/src/compiler/crystal/semantic/type_guess_visitor.cr @@ -1077,6 +1077,14 @@ module Crystal @program.int32 end + def guess_type(node : AlignOf) + @program.int32 + end + + def guess_type(node : InstanceAlignOf) + @program.int32 + end + def guess_type(node : OffsetOf) @program.int32 end @@ -1275,7 +1283,7 @@ module Crystal false end - def visit(node : InstanceSizeOf | SizeOf | OffsetOf | TypeOf | PointerOf) + def visit(node : InstanceSizeOf | SizeOf | InstanceAlignOf | AlignOf | OffsetOf | TypeOf | PointerOf) false end diff --git a/src/compiler/crystal/semantic/type_lookup.cr b/src/compiler/crystal/semantic/type_lookup.cr index 0db48a0298aa..ba538ed0323d 100644 --- a/src/compiler/crystal/semantic/type_lookup.cr +++ b/src/compiler/crystal/semantic/type_lookup.cr @@ -247,7 +247,7 @@ class Crystal::Type type_var.raise "can only splat tuple type, not #{splat_type}" end next - when SizeOf, InstanceSizeOf, OffsetOf + when SizeOf, InstanceSizeOf, AlignOf, InstanceAlignOf, OffsetOf next unless @raise type_var.raise "can't use #{type_var} as a generic type argument" diff --git a/src/compiler/crystal/syntax/ast.cr b/src/compiler/crystal/syntax/ast.cr index 7b37c369927f..d0be18606714 100644 --- a/src/compiler/crystal/syntax/ast.cr +++ b/src/compiler/crystal/syntax/ast.cr @@ -1202,6 +1202,18 @@ module Crystal end end + class AlignOf < UnaryExpression + def clone_without_location + AlignOf.new(@exp.clone) + end + end + + class InstanceAlignOf < UnaryExpression + def clone_without_location + InstanceAlignOf.new(@exp.clone) + end + end + class Out < UnaryExpression def clone_without_location Out.new(@exp.clone) diff --git a/src/compiler/crystal/syntax/lexer.cr b/src/compiler/crystal/syntax/lexer.cr index cdb0f1a33e1c..21c3ab42c804 100644 --- a/src/compiler/crystal/syntax/lexer.cr +++ b/src/compiler/crystal/syntax/lexer.cr @@ -586,8 +586,19 @@ module Crystal return check_ident_or_keyword(:abstract, start) end when 'l' - if char_sequence?('i', 'a', 's') - return check_ident_or_keyword(:alias, start) + if next_char == 'i' + case next_char + when 'a' + if next_char == 's' + return check_ident_or_keyword(:alias, start) + end + when 'g' + if char_sequence?('n', 'o', 'f') + return check_ident_or_keyword(:alignof, start) + end + else + # scan_ident + end end when 's' case peek_next_char @@ -719,8 +730,19 @@ module Crystal return check_ident_or_keyword(:include, start) end when 's' - if char_sequence?('t', 'a', 'n', 'c', 'e', '_', 's', 'i', 'z', 'e', 'o', 'f') - return check_ident_or_keyword(:instance_sizeof, start) + if char_sequence?('t', 'a', 'n', 'c', 'e', '_') + case next_char + when 's' + if char_sequence?('i', 'z', 'e', 'o', 'f') + return check_ident_or_keyword(:instance_sizeof, start) + end + when 'a' + if char_sequence?('l', 'i', 'g', 'n', 'o', 'f') + return check_ident_or_keyword(:instance_alignof, start) + end + else + # scan_ident + end end else # scan_ident diff --git a/src/compiler/crystal/syntax/parser.cr b/src/compiler/crystal/syntax/parser.cr index d2d7816dd6b9..751608468cd5 100644 --- a/src/compiler/crystal/syntax/parser.cr +++ b/src/compiler/crystal/syntax/parser.cr @@ -1189,6 +1189,10 @@ module Crystal check_type_declaration { parse_sizeof } when .instance_sizeof? check_type_declaration { parse_instance_sizeof } + when .alignof? + check_type_declaration { parse_alignof } + when .instance_alignof? + check_type_declaration { parse_instance_alignof } when .offsetof? check_type_declaration { parse_offsetof } when .typeof? @@ -4111,7 +4115,7 @@ module Crystal .extend?, .class?, .struct?, .module?, .enum?, .while?, .until?, .return?, .next?, .break?, .lib?, .fun?, .alias?, .pointerof?, .sizeof?, .offsetof?, .instance_sizeof?, .typeof?, .private?, .protected?, .asm?, .out?, - .self?, Keyword::IN, .end? + .self?, Keyword::IN, .end?, .alignof?, .instance_alignof? true else false @@ -4123,7 +4127,7 @@ module Crystal "extend", "class", "struct", "module", "enum", "while", "until", "return", "next", "break", "lib", "fun", "alias", "pointerof", "sizeof", "offsetof", "instance_sizeof", "typeof", "private", "protected", "asm", "out", - "self", "in", "end" + "self", "in", "end", "alignof", "instance_alignof" true else false @@ -5139,7 +5143,11 @@ module Crystal args << parse_type_splat { parse_type_arg } end - has_int = args.any? { |arg| arg.is_a?(NumberLiteral) || arg.is_a?(SizeOf) || arg.is_a?(InstanceSizeOf) || arg.is_a?(OffsetOf) } + has_int = args.any? do |arg| + arg.is_a?(NumberLiteral) || arg.is_a?(SizeOf) || arg.is_a?(InstanceSizeOf) || + arg.is_a?(AlignOf) || arg.is_a?(InstanceAlignOf) || arg.is_a?(OffsetOf) + end + if @token.type.op_minus_gt? && !has_int args = [parse_proc_type_output(args, args.first.location)] of ASTNode end @@ -5221,6 +5229,10 @@ module Crystal parse_sizeof when .keyword?(:instance_sizeof) parse_instance_sizeof + when .keyword?(:alignof) + parse_alignof + when .keyword?(:instance_alignof) + parse_instance_alignof when .keyword?(:offsetof) parse_offsetof else @@ -5875,6 +5887,14 @@ module Crystal parse_sizeof InstanceSizeOf end + def parse_alignof + parse_sizeof AlignOf + end + + def parse_instance_alignof + parse_sizeof InstanceAlignOf + end + def parse_sizeof(klass) sizeof_location = @token.location next_token_skip_space diff --git a/src/compiler/crystal/syntax/to_s.cr b/src/compiler/crystal/syntax/to_s.cr index 69a936c22e5a..8ea364d3f991 100644 --- a/src/compiler/crystal/syntax/to_s.cr +++ b/src/compiler/crystal/syntax/to_s.cr @@ -1257,6 +1257,20 @@ module Crystal false end + def visit(node : AlignOf) + @str << "alignof(" + node.exp.accept(self) + @str << ')' + false + end + + def visit(node : InstanceAlignOf) + @str << "instance_alignof(" + node.exp.accept(self) + @str << ')' + false + end + def visit(node : OffsetOf) @str << "offsetof(" node.offsetof_type.accept(self) diff --git a/src/compiler/crystal/syntax/token.cr b/src/compiler/crystal/syntax/token.cr index 125ec14ee589..7b5da2091b67 100644 --- a/src/compiler/crystal/syntax/token.cr +++ b/src/compiler/crystal/syntax/token.cr @@ -5,6 +5,7 @@ module Crystal enum Keyword ABSTRACT ALIAS + ALIGNOF ANNOTATION AS AS_QUESTION @@ -27,6 +28,7 @@ module Crystal IF IN INCLUDE + INSTANCE_ALIGNOF INSTANCE_SIZEOF IS_A_QUESTION LIB diff --git a/src/compiler/crystal/syntax/transformer.cr b/src/compiler/crystal/syntax/transformer.cr index 299e1c53c6ad..f391b8196e36 100644 --- a/src/compiler/crystal/syntax/transformer.cr +++ b/src/compiler/crystal/syntax/transformer.cr @@ -165,17 +165,7 @@ module Crystal node end - def transform(node : PointerOf) - node.exp = node.exp.transform(self) - node - end - - def transform(node : SizeOf) - node.exp = node.exp.transform(self) - node - end - - def transform(node : InstanceSizeOf) + def transform(node : UnaryExpression) node.exp = node.exp.transform(self) node end @@ -394,11 +384,6 @@ module Crystal node end - def transform(node : Out) - node.exp = node.exp.transform(self) - node - end - def transform(node : Nop) node end @@ -499,11 +484,6 @@ module Crystal node end - def transform(node : Not) - node.exp = node.exp.transform(self) - node - end - def transform(node : TupleLiteral) transform_many node.elements node @@ -539,16 +519,6 @@ module Crystal node end - def transform(node : Splat) - node.exp = node.exp.transform(self) - node - end - - def transform(node : DoubleSplat) - node.exp = node.exp.transform(self) - node - end - def transform(node : VisibilityModifier) node.exp = node.exp.transform(self) node diff --git a/src/compiler/crystal/tools/formatter.cr b/src/compiler/crystal/tools/formatter.cr index ae16d4bec0fd..fc3be7a4cf66 100644 --- a/src/compiler/crystal/tools/formatter.cr +++ b/src/compiler/crystal/tools/formatter.cr @@ -3959,6 +3959,14 @@ module Crystal visit Call.new(nil, "instance_sizeof", node.exp) end + def visit(node : AlignOf) + visit Call.new(nil, "alignof", node.exp) + end + + def visit(node : InstanceAlignOf) + visit Call.new(nil, "instance_alignof", node.exp) + end + def visit(node : OffsetOf) visit Call.new(nil, "offsetof", [node.offsetof_type, node.offset]) end diff --git a/src/llvm/lib_llvm/core.cr b/src/llvm/lib_llvm/core.cr index a0cfb708d515..be4cb16f5d49 100644 --- a/src/llvm/lib_llvm/core.cr +++ b/src/llvm/lib_llvm/core.cr @@ -120,6 +120,7 @@ lib LibLLVM fun const_struct_in_context = LLVMConstStructInContext(c : ContextRef, constant_vals : ValueRef*, count : UInt, packed : Bool) : ValueRef fun const_array = LLVMConstArray(element_ty : TypeRef, constant_vals : ValueRef*, length : UInt) : ValueRef + fun align_of = LLVMAlignOf(ty : TypeRef) : ValueRef fun size_of = LLVMSizeOf(ty : TypeRef) : ValueRef fun get_global_parent = LLVMGetGlobalParent(global : ValueRef) : ModuleRef diff --git a/src/llvm/type.cr b/src/llvm/type.cr index dc5f127492ab..06c36ff5796d 100644 --- a/src/llvm/type.cr +++ b/src/llvm/type.cr @@ -21,6 +21,15 @@ struct LLVM::Type end end + def alignment + # Asking the alignment of void crashes the program, we definitely don't want that + if void? + context.int64.const_int(1) + else + Value.new LibLLVM.align_of(self) + end + end + def kind : LLVM::Type::Kind LibLLVM.get_type_kind(self) end From 7592b4e6d46b3c9963437baac8b2e17d977ac20b Mon Sep 17 00:00:00 2001 From: Quinton Miller Date: Thu, 14 Dec 2023 19:42:05 +0800 Subject: [PATCH 2/2] fixup --- src/compiler/crystal/syntax/transformer.cr | 42 +++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/compiler/crystal/syntax/transformer.cr b/src/compiler/crystal/syntax/transformer.cr index f391b8196e36..1dc584ebfa17 100644 --- a/src/compiler/crystal/syntax/transformer.cr +++ b/src/compiler/crystal/syntax/transformer.cr @@ -165,7 +165,27 @@ module Crystal node end - def transform(node : UnaryExpression) + def transform(node : PointerOf) + node.exp = node.exp.transform(self) + node + end + + def transform(node : SizeOf) + node.exp = node.exp.transform(self) + node + end + + def transform(node : InstanceSizeOf) + node.exp = node.exp.transform(self) + node + end + + def transform(node : AlignOf) + node.exp = node.exp.transform(self) + node + end + + def transform(node : InstanceAlignOf) node.exp = node.exp.transform(self) node end @@ -384,6 +404,11 @@ module Crystal node end + def transform(node : Out) + node.exp = node.exp.transform(self) + node + end + def transform(node : Nop) node end @@ -484,6 +509,11 @@ module Crystal node end + def transform(node : Not) + node.exp = node.exp.transform(self) + node + end + def transform(node : TupleLiteral) transform_many node.elements node @@ -519,6 +549,16 @@ module Crystal node end + def transform(node : Splat) + node.exp = node.exp.transform(self) + node + end + + def transform(node : DoubleSplat) + node.exp = node.exp.transform(self) + node + end + def transform(node : VisibilityModifier) node.exp = node.exp.transform(self) node