Skip to content

Commit

Permalink
Support alignof and instance_alignof
Browse files Browse the repository at this point in the history
  • Loading branch information
HertzDevil committed Dec 13, 2023
1 parent 59095ff commit d5d2e3f
Show file tree
Hide file tree
Showing 29 changed files with 431 additions and 73 deletions.
101 changes: 101 additions & 0 deletions spec/compiler/codegen/sizeof_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
2 changes: 2 additions & 0 deletions spec/compiler/formatter/formatter_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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)"

Expand Down
18 changes: 18 additions & 0 deletions spec/compiler/interpreter/sizeof_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 6 additions & 0 deletions spec/compiler/parser/parser_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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'"
Expand Down Expand Up @@ -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)
Expand Down
26 changes: 10 additions & 16 deletions spec/compiler/semantic/sizeof_spec.cr
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions spec/std/crystal/syntax_highlighter/colorize_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions spec/std/crystal/syntax_highlighter/html_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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, %(<span class="k">#{kw}</span>)
Expand Down
26 changes: 25 additions & 1 deletion src/compiler/crystal/codegen/codegen.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/crystal/codegen/llvm_builder_helper.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
6 changes: 5 additions & 1 deletion src/compiler/crystal/codegen/llvm_typer.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
21 changes: 19 additions & 2 deletions src/compiler/crystal/interpreter/compiler.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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?
Expand Down
24 changes: 24 additions & 0 deletions src/compiler/crystal/interpreter/context.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
Loading

0 comments on commit d5d2e3f

Please sign in to comment.