Last active
May 24, 2024 12:55
-
-
Save ysbaddaden/cf58d33e316067a9e4ad886976cbdc98 to your computer and use it in GitHub Desktop.
Parse LLVMDataLayout string + alignof, sizeof & offsetof ints, floats & aggregates + ABI for Crystal::Type(s)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "./data_layout" | |
# I must require the WHOLE COMPILER just to load crystal/types, and then it | |
# takes as long as compiling the compiler to compile just this file and to do | |
# nothing. | |
require "compiler/requires" | |
class Crystal::ABI | |
@nil_type : DataLayout::Aggregate? | |
@proc_type : DataLayout::Aggregate? | |
def initialize(@data_layout : DataLayout) | |
# OPTIMIZE: are Crystal::Type unique across a compilation? in which case the | |
# cache key could be the class itself (no need to build a String | |
# everytime) | |
@cache = {} of String => DataLayout::Aggregate | |
end | |
# helpers | |
# the nil type is represented as an empty struct | |
protected def nil_type | |
@nil_type ||= @data_layout.aggregate | |
end | |
# proc types are represented as a pair of void pointers | |
protected def proc_type | |
@proc_type ||= @data_layout.aggregate.tap do |aggregate| | |
aggregate.add @data_layout.size_of_pointer, @data_layout.align_of_pointer | |
aggregate.add @data_layout.size_of_pointer, @data_layout.align_of_pointer | |
end | |
end | |
protected def aggregate_for(type : TupleInstanceType) | |
@cache[type.to_s] ||= @data_layout.aggregate(type.packed?).tap do |aggregate| | |
type.tuple_types.each do |t| | |
aggregate.add size_of(t) * 8, align_of(t) * 8 | |
end | |
end | |
end | |
protected def aggregate_for(type : NamedTupleInstanceType) | |
@cache[type.to_s] ||= @data_layout.aggregate(type.packed?).tap do |aggregate| | |
type.entries.each do |entry| | |
aggregate.add size_of(entry.type) * 8, align_of(entry.type) * 8 | |
end | |
end | |
end | |
protected def aggregate_for(type : InstanceVarContainer) | |
@cache[type.to_s] ||= @data_layout.aggregate(type.packed?).tap do |aggregate| | |
type.all_instance_vars.each do |(_, t)| | |
aggregate.add size_of(t) * 8, align_of(t) * 8 | |
end | |
end | |
end | |
protected def aggregate_for(type : Type) : NoReturn | |
raise "BUG: can't build aggregate for #{type}" | |
end | |
# alignof | |
def align_of(type : NoReturnType) : UInt32 | |
1_u32 | |
end | |
def align_of(type : VoidType) : UInt32 | |
# We need `alignof(Void)` to be 1 because it is effectively the smallest | |
# possible alignment for any type. | |
1_u32 | |
end | |
def align_of(type : NilType) : UInt32 | |
nil_type.alignment // 8 | |
end | |
def align_of(type : BoolType) : UInt32 | |
@data_layout.align_of_int(1) // 8 | |
end | |
def align_of(type : CharType) : UInt32 | |
@data_layout.align_of_int(32) // 8 | |
end | |
def align_of(type : IntegerType) : UInt32 | |
@data_layout.align_of_int(type.bits) // 8 | |
end | |
def align_of(type : FloatType) : UInt32 | |
@data_layout.align_of_float(type.bytes * 8) // 8 | |
end | |
def align_of(type : SymbolType) : UInt32 | |
@data_layout.align_of_int(32) // 8 | |
end | |
def align_of(type : EnumType) : UInt32 | |
align_of type.base_type | |
end | |
def align_of(type : ProcInstanceType | NilableProcType) : UInt32 | |
proc_type.alignment // 8 | |
end | |
def align_of(type : InstanceVarContainer) : UInt32 | |
if type.struct? | |
instance_align_of(type) | |
else | |
# references are pointers | |
@data_layout.align_of_pointer // 8 | |
end | |
end | |
def align_of(type : LibType) : UInt32 | |
@data_layout.align_of_int(32) // 8 | |
end | |
def align_of(type : MetaclassType | GenericClassInstanceMetaclassType | GenericModuleInstanceMetaclassType | VirtualMetaclassType) : UInt32 | |
@data_layout.align_of_int(32) // 8 | |
end | |
def align_of(type : PointerInstanceType) : UInt32 | |
@data_layout.align_of_pointer // 8 | |
end | |
def align_of(type : StaticArrayInstanceType) : UInt32 | |
align_of type.element_type | |
end | |
def align_of(type : TupleInstanceType | NamedTupleInstanceType) : UInt32 | |
aggregate_for(type).alignment // 8 | |
end | |
def align_of(type : NilableType) : UInt32 | |
align_of type.not_nil_type | |
end | |
def align_of(type : ReferenceUnionType | NilableReferenceUnionType) : UInt32 | |
@data_layout.align_of_pointer # i32* | |
end | |
def align_of(type : TypeDefType) : UInt32 | |
align_of type.typedef | |
end | |
def align_of(type : VirtualType) : UInt32 | |
@data_layout.align_of_pointer // 8 # i32* | |
end | |
def align_of(type : AliasType) : UInt32 | |
align_of type.remove_alias | |
end | |
def align_of(type : ReferenceStorageType) : UInt32 | |
align_of type.reference_type | |
end | |
def align_of(type : NonGenericModuleType | GenericClassType) : UInt32 | |
@data_layout.align_of_int(1) // 8 | |
end | |
def align_of(type : Type) : NoReturn | |
raise "BUG: can't alignof #{type}" | |
end | |
private def align_of(type : MetaTypeVar) : UInt32 | |
align_of type.type | |
end | |
# instance_alignof | |
def instance_align_of(type : InstanceVarContainer) : UInt32 | |
type = type.remove_indirection | |
if type.extern_union? | |
# largest alignment from all the union types | |
type.instance_vars.reduce(0_u32) do |max, (_, t)| | |
align = align_of(t) | |
align > max ? align : max | |
end | |
else | |
aggregate_for(type).alignment // 8 | |
end | |
end | |
def instance_align_of(type : Type) : NoReturn | |
raise "BUG: can't instance_alignof #{type}" | |
end | |
# sizeof | |
def size_of(type : NoReturnType) : UInt64 | |
0_u64 | |
end | |
def size_of(type : VoidType) : UInt64 | |
# 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_u64 | |
end | |
def size_of(type : NilType) : UInt64 | |
nil_type.size // 8 | |
end | |
def size_of(type : BoolType) : UInt64 | |
@data_layout.size_of_int(1) // 8 | |
end | |
def size_of(type : CharType) : UInt64 | |
@data_layout.size_of_int(32) // 8 | |
end | |
def size_of(type : IntegerType) : UInt64 | |
@data_layout.size_of_int(type.bits) // 8 | |
end | |
def size_of(type : FloatType) : UInt64 | |
@data_layout.size_of_float(type.bytes * 8) // 8 | |
end | |
def size_of(type : SymbolType) : UInt64 | |
@data_layout.size_of_int(32) // 8 | |
end | |
def size_of(type : EnumType) : UInt64 | |
size_of type.base_type | |
end | |
def size_of(type : ProcInstanceType | NilableProcType) : UInt64 | |
proc_type.size // 8 | |
end | |
private def size_of(type : MetaTypeVar) : UInt64 | |
size_of type.type | |
end | |
def size_of(type : InstanceVarContainer) : UInt64 | |
if type.struct? | |
instance_size_of(type) | |
else | |
# references are mere pointers | |
@data_layout.size_of_pointer // 8 | |
end | |
end | |
def size_of(type : LibType) : UInt64 | |
@data_layout.size_of_int(32) // 8 | |
end | |
def size_of(type : MetaclassType | GenericClassInstanceMetaclassType | GenericModuleInstanceMetaclassType | VirtualMetaclassType) : UInt64 | |
@data_layout.size_of_int(32) // 8 | |
end | |
def size_of(type : PointerInstanceType) : UInt64 | |
@data_layout.size_of_pointer // 8 | |
end | |
def size_of(type : StaticArrayInstanceType) : UInt64 | |
size_of(type.element_type) * type.size.as(NumberLiteral).value.to_u64 | |
end | |
def size_of(type : TupleInstanceType | NamedTupleInstanceType) : UInt64 | |
aggregate_for(type).size // 8 | |
end | |
def size_of(type : NilableType) : UInt64 | |
size_of type.not_nil_type | |
end | |
def size_of(type : ReferenceUnionType | NilableReferenceUnionType) : UInt64 | |
@data_layout.size_of_pointer // 8 # i32* | |
end | |
def size_of(type : TypeDefType) : UInt64 | |
size_of type.typedef | |
end | |
def size_of(type : VirtualType) : UInt64 | |
@data_layout.size_of_pointer // 8 # i32* | |
end | |
def size_of(type : AliasType) : UInt64 | |
size_of type.remove_alias | |
end | |
def size_of(type : ReferenceStorageType) : UInt64 | |
size_of type.reference_type | |
end | |
def size_of(type : NonGenericModuleType | GenericClassType) : UInt64 | |
@data_layout.size_of_int(1) // 8 | |
end | |
def size_of(type : Type) : UInt64 | |
raise "BUG: can't sizeof #{type}" | |
end | |
# instance_sizeof | |
def instance_size_of(type : InstanceVarContainer) : UInt64 | |
type = type.remove_indirection | |
if type.extern_union? | |
# largest type from all the union types | |
type.instance_vars.reduce(0_u64) do |max, (_, t)| | |
size = size_of(t) | |
size > max ? size : max | |
end | |
else | |
aggregate_for(type).size // 8 | |
end | |
end | |
def instance_size_of(type : Type) : NoReturn | |
raise "BUG: can't instance_sizeof #{type}" | |
end | |
# offsetof | |
def offset_of(type : StaticArrayInstanceType, index : Int32) : UInt64 | |
0_u64 | |
end | |
def offset_of(type : TupleInstanceType | NamedTupleInstanceType, index : Int32) : UInt64 | |
aggregate_for(type).offset(index) // 8 | |
end | |
def offset_of(type : InstanceVarContainer, index : Int32) : UInt64 | |
if type.extern_union? | |
0_u64 | |
else | |
raise "BUG: can't compute offsetof #{type}" | |
end | |
end | |
def offset_of(type : Type) : NoReturn | |
raise "BUG: can't offsetof #{type}" | |
end | |
# instance_offsetof | |
def instance_offset_of(type : InstanceVarContainer, index : Int32) : UInt64 | |
type = type.remove_indirection | |
if type.extern_union? | |
raise "BUG: can't compute instance_offsetof #{type}" | |
else | |
aggregate_for(type).offset(index) // 8 | |
end | |
end | |
def instance_offset_of(type : Type) : NoReturn | |
raise "BUG: can't instance_offsetof #{type}" | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "./abi" | |
require "spec" | |
require "llvm" | |
LLVM.init_x86 | |
private def semantic(source : String, *, flags = nil) | |
program = Crystal::Program.new | |
program.color = false | |
program.wants_doc = false | |
program.flags.concat(flags.split) if flags | |
parser = Crystal::Parser.new(source, warnings: nil) | |
parser.wants_doc = false | |
node = parser.parse | |
node = program.normalize(node) | |
node = program.semantic(node) | |
node | |
end | |
private ABI_CACHE = {} of String => Crystal::ABI | |
private TRIPLE = "x86_64-linux-gnu" | |
private def abi_for(triple : String) | |
ABI_CACHE[triple] ||= begin | |
machine = LLVM::Target.from_triple(triple).create_target_machine(triple) | |
data_layout = DataLayout.new(machine.data_layout.to_data_layout_string) | |
Crystal::ABI.new(data_layout) | |
end | |
end | |
describe "ABI" do | |
it "NumberLiteral" do | |
abi = abi_for(TRIPLE) | |
node = semantic("1_u8") | |
p! abi.align_of(node.type) | |
p! abi.size_of(node.type) | |
end | |
# TODO: write actual specs | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Parses a LLVM data layout string, and implements low level support for | |
# calculating the alignment and size of simple types (integers, floats, | |
# aggregates) as well as the offset of each element of an aggregate. | |
# | |
# NOTE: all sizes are in bits! | |
# FIXME: all sizes shall be in bytes to avoid lots of divide and multiply by 8 | |
# to convert + avoid confusing when ABI usually needs bytes but the | |
# memory layout uses bits | |
# TODO: vectors (SIMD) | |
class DataLayout | |
enum Mangling : Int32 | |
{% begin %} | |
ELF = {{'e'.ord}} | |
GOFF = {{'l'.ord}} | |
Mips = {{'m'.ord}} | |
Mach_O = {{'o'.ord}} | |
Windows_x86_COFF = {{'x'.ord}} | |
Windows_COFF = {{'w'.ord}} | |
XCOFF = {{'a'.ord}} | |
{% end %} | |
UNKNOWN = -1 | |
end | |
enum FunctionPointerAlignment : Int32 | |
{% begin %} | |
Implicit = {{'i'.ord}} | |
Explicit = {{'n'.ord}} | |
{% end %} | |
end | |
alias PointerAlignment = {address_space: Int32, size: Int32, abi_alignment: UInt32, preferred_alignment: UInt32, index_size: Int32} | |
alias SizeAlignment = {size: Int32, abi_alignment: UInt32, preferred_alignment: UInt32} | |
alias AggregateAlignment = {abi_alignment: UInt32, preferred_alignment: UInt32} | |
# :nodoc: | |
DEFAULT_INTEGER_ALIGNMENTS = StaticArray[ | |
{size: 8, abi_alignment: 8_u32, preferred_alignment: 8_u32}, | |
{size: 16, abi_alignment: 16_u32, preferred_alignment: 16_u32}, | |
{size: 32, abi_alignment: 32_u32, preferred_alignment: 32_u32}, | |
{size: 64, abi_alignment: 32_u32, preferred_alignment: 32_u32}, | |
] | |
# :nodoc: | |
DEFAULT_FLOAT_ALIGNMENTS = StaticArray[ | |
{size: 16, abi_alignment: 16_u32, preferred_alignment: 16_u32}, | |
{size: 32, abi_alignment: 32_u32, preferred_alignment: 32_u32}, | |
{size: 64, abi_alignment: 64_u32, preferred_alignment: 64_u32}, | |
{size: 128, abi_alignment: 128_u32, preferred_alignment: 128_u32}, | |
] | |
# :nodoc: | |
DEFAULT_POINTER = {size: 64, abi_alignment: 64_u32, preferred_alignment: 64_u32, index_size: 64} | |
# :nodoc: | |
DEFAULT_AGGREGATE = {abi_alignment: 8_u32, preferred_alignment: 8_u32} | |
getter! endianness : IO::ByteFormat | |
getter! natural_stack_alignment : Int32 | |
getter program_address_space = 0 | |
getter globals_address_space = 0 | |
getter alloca_address_space = 0 | |
getter pointer_alignments = Array(PointerAlignment).new | |
getter integer_alignments = Array(SizeAlignment).new | |
getter vector_alignments = Array(SizeAlignment).new | |
getter float_alignments = Array(SizeAlignment).new | |
getter! aggregate_alignment : AggregateAlignment | |
getter! mangling : Mangling | |
getter native_integer_widths = Array(Int32).new | |
getter non_integral_pointers = Array(Int32).new | |
getter! function_pointer_alignment : FunctionPointerAlignment | |
getter! function_pointer_multiplier : Int32? | |
def initialize(data_layout_string : String) | |
data_layout_string.split('-') do |str| | |
case str | |
when "e" | |
@endianness = IO::ByteFormat::LittleEndian | |
when "E" | |
@endianness = IO::ByteFormat::BigEndian | |
when .starts_with?('S') | |
@natural_stack_alignment = str[1..].to_i | |
when .starts_with?('P') | |
@program_address_space = str[1..].to_i | |
when .starts_with?('G') | |
@globals_address_space = str[1..].to_i | |
when .starts_with?('A') | |
@alloca_address_space = str[1..].to_i | |
when .starts_with?('p') | |
@pointer_alignments << parse_pointer_alignment(str) | |
when .starts_with?('i') | |
@integer_alignments << parse_size_alignment(str) | |
when .starts_with?('v') | |
@vector_alignments << parse_size_alignment(str) | |
when .starts_with?('f') | |
@float_alignments << parse_size_alignment(str) | |
when .starts_with?('a') | |
@aggregate_alignment = parse_aggregate_alignment(str) | |
when .starts_with?('F') | |
@function_pointer_alignment = FunctionPointerAlignment.new(str[1].ord) | |
@function_pointer_multiplier = str[2..].to_i | |
when .starts_with?('m') | |
@mangling = Mangling.new(str[2].ord) | |
when .starts_with?("ni:") | |
str[3..].split(':').each { |n| @non_integral_pointers << n.to_i } | |
when .starts_with?('n') | |
str[1..].split(':').each { |n| @native_integer_widths << n.to_i } | |
end | |
end | |
@pointer_alignments.sort_by! { |x| x[:address_space] } | |
@integer_alignments.sort_by! { |x| x[:size] } | |
@vector_alignments.sort_by! { |x| x[:size] } | |
@float_alignments.sort_by! { |x| x[:size] } | |
@native_integer_widths.sort! | |
end | |
private def parse_pointer_alignment(str) | |
parts = str[1..].split(':') | |
size = parts[1].to_i | |
abi_alignment = parts[2].to_u32 | |
{ | |
address_space: parts[0].to_i? || 0, | |
size: size, | |
abi_alignment: abi_alignment, | |
preferred_alignment: parts[3]?.try(&.to_u32?) || abi_alignment, | |
index_size: parts[4]?.try(&.to_i?) || size, | |
} | |
end | |
private def parse_size_alignment(str) | |
parts = str[1..].split(':') | |
size = parts[0].to_i | |
abi_alignment = parts[1].to_u32 | |
{ | |
size: size, | |
abi_alignment: abi_alignment, | |
preferred_alignment: parts[2]?.try(&.to_u32?) || abi_alignment, | |
} | |
end | |
private def parse_aggregate_alignment(str) | |
parts = str[2..].split(':') | |
abi_alignment = parts[0].to_u32 | |
{ | |
abi_alignment: abi_alignment, | |
preferred_alignment: parts[1]?.try(&.to_u32?) || abi_alignment, | |
} | |
end | |
# Computes the size and alignment of an aggregate, as well as the offset of | |
# each element. | |
# | |
# NOTE: all sizes are in bits! | |
# FIXME: all sizes shall be in bytes to avoid lots of divide and multiply by 8 | |
# to convert + avoid confusing when ABI usually needs bytes but the | |
# memory layout uses bits | |
class Aggregate | |
protected def initialize(@default_alignment : UInt32, @packed : Bool) | |
@size = 0_u64 | |
@alignment = @default_alignment | |
@offsets = [] of UInt64 | |
end | |
def add(size : UInt64, align : UInt32) : Nil | |
offset = @size | |
if @packed | |
@size += size | |
else | |
@alignment = align if align > @alignment | |
offset = DataLayout.aligned_size(@size, align) | |
@size = offset + size | |
end | |
@offsets << offset | |
end | |
def alignment : UInt32 | |
@alignment | |
end | |
def size : UInt64 | |
DataLayout.aligned_size(@size, @alignment) | |
end | |
def offset(index : UInt32) : UInt64 | |
@offsets[index] | |
end | |
end | |
def aggregate(packed : Bool = false) : Aggregate | |
Aggregate.new(default_align_of_aggregate, packed) | |
end | |
# align_of | |
def align_of_int(bits) : UInt32 | |
align_of(bits, @integer_alignments, DEFAULT_INTEGER_ALIGNMENTS) | |
end | |
def align_of_float(bits) : UInt32 | |
align_of(bits, @float_alignments, DEFAULT_FLOAT_ALIGNMENTS) | |
end | |
private def align_of(bits, specified, defaults) : UInt32 | |
specified.each do |a| | |
return a[:abi_alignment] if bits == a[:size] | |
end | |
defaults.each do |a| | |
if bits <= a[:size] | |
c = specified.find { |x| x[:size] == a[:size] } | |
return (c || a)[:abi_alignment] | |
end | |
end | |
specified.each do |a| | |
return a[:abi_alignment] if bits < a[:size] | |
end | |
a = specified.last? || defaults.last | |
a[:abi_alignment] | |
end | |
def align_of_pointer(address_space = 0) : UInt32 | |
if a = @pointer_alignments.find { |x| x[:address_space] == address_space } | |
a[:abi_alignment] | |
else | |
DEFAULT_POINTER[:abi_alignment] | |
end | |
end | |
# size_of | |
def size_of_int(bits) : UInt64 | |
DataLayout.aligned_size(bits.to_u64, align_of_int(bits)) | |
end | |
def size_of_float(bits) : UInt64 | |
DataLayout.aligned_size(bits.to_u64, align_of_float(bits)) | |
end | |
def size_of_pointer(address_space = 0) : UInt64 | |
if a = @pointer_alignments.find { |x| x[:address_space] == address_space } | |
a[:size].to_u64 | |
else | |
DEFAULT_POINTER[:size].to_u64 | |
end | |
end | |
# helpers | |
protected def default_align_of_aggregate : UInt32 | |
(@aggregate_alignment || DEFAULT_AGGREGATE)[:abi_alignment].to_u32.clamp(8_u32..) | |
end | |
protected def self.aligned_size(bits, align) | |
if bits % align == 0 | |
bits | |
else | |
(bits // align + 1) * align | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require "spec" | |
require "llvm" | |
require "./data_layout" | |
LLVM.init_aarch64 | |
LLVM.init_arm | |
LLVM.init_avr | |
LLVM.init_webassembly | |
LLVM.init_x86 | |
private LAYOUTS_CACHE = {} of String => {LLVM::Context, LLVM::TargetData, DataLayout} | |
private def machine_for(triple, &) | |
data = LAYOUTS_CACHE[triple] ||= begin | |
target = LLVM::Target.from_triple(triple) | |
machine = target.create_target_machine(triple) | |
ctx = LLVM::Context.new | |
target_data = machine.data_layout | |
data_layout = DataLayout.new(target_data.to_data_layout_string) | |
{ctx, target_data, data_layout} | |
end | |
yield(*data) | |
end | |
describe "memory layout" do | |
{% for triple in %w[ | |
aarch64-linux-gnu | |
aarch64-apple-darwin | |
arm-linux-gnu | |
armv7-linux-gnu | |
avr-unknown-unknown | |
i686-linux-gnu | |
i686-windows-msvc | |
wasm32-unknown-wasi | |
x86_64-linux-gnu | |
x86_64-windows-msvc | |
x86_64-apple-darwin | |
] %} | |
describe {{triple}} do | |
describe "align_of" do | |
it "integers" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
[1, 7, 8, 15, 16, 24, 32, 64, 65, 128, 256].each do |n| | |
crystal_layout.align_of_int(n).should eq llvm_layout.abi_alignment(ctx.int(n)) * 8 | |
end | |
end | |
end | |
it "floats" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
crystal_layout.align_of_float(16).should eq llvm_layout.abi_alignment(ctx.half) * 8 | |
crystal_layout.align_of_float(32).should eq llvm_layout.abi_alignment(ctx.float) * 8 | |
crystal_layout.align_of_float(64).should eq llvm_layout.abi_alignment(ctx.double) * 8 | |
{% if triple.starts_with?("x86_64") || triple =~ /^i[3456]?86/ %} | |
crystal_layout.align_of_float(80).should eq llvm_layout.abi_alignment(ctx.x86_fp80) * 8 | |
{% end %} | |
crystal_layout.align_of_float(128).should eq llvm_layout.abi_alignment(ctx.fp128) * 8 | |
end | |
end | |
describe "pointer" do | |
it "default address space (0)" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
crystal_layout.align_of_pointer.should eq llvm_layout.abi_alignment(ctx.void_pointer) * 8 | |
end | |
end | |
it "specific address spaces" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
crystal_layout.pointer_alignments.each do |x| | |
align = crystal_layout.align_of_pointer(x[:address_space]) | |
align.should eq llvm_layout.abi_alignment(ctx.void_pointer(x[:address_space])) * 8 | |
end | |
end | |
end | |
end | |
describe "aggregate" do | |
it "empty struct" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
p! agg = crystal_layout.aggregate | |
agg.alignment.should eq llvm_layout.abi_alignment(ctx.struct([] of LLVM::Type)) * 8 | |
end | |
end | |
it "one property" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate | |
agg.add(crystal_layout.size_of_int(32), crystal_layout.align_of_int(32)) | |
agg.alignment.should eq llvm_layout.abi_alignment(ctx.struct([ctx.int32] of LLVM::Type)) * 8 | |
end | |
end | |
it "a few properties" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate | |
agg.add crystal_layout.size_of_int(8), crystal_layout.align_of_int(8) | |
agg.add crystal_layout.size_of_int(32), crystal_layout.align_of_int(32) | |
agg.add crystal_layout.size_of_int(16), crystal_layout.align_of_int(16) | |
agg.alignment.should eq llvm_layout.abi_alignment(ctx.struct([ | |
ctx.int8, | |
ctx.int32, | |
ctx.int16, | |
] of LLVM::Type)) * 8 | |
end | |
end | |
it "large properties" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate | |
agg.add crystal_layout.size_of_int(8), crystal_layout.align_of_int(8) | |
agg.add crystal_layout.size_of_int(512), crystal_layout.align_of_int(512) | |
agg.alignment.should eq llvm_layout.abi_alignment(ctx.struct([ | |
ctx.int8, | |
ctx.int(512), | |
] of LLVM::Type)) * 8 | |
end | |
end | |
it "many properties" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate | |
agg.add crystal_layout.size_of_int(8), crystal_layout.align_of_int(8) | |
agg.add crystal_layout.size_of_int(32), crystal_layout.align_of_int(32) | |
agg.add crystal_layout.size_of_float(32),crystal_layout.align_of_float(32) | |
agg.add crystal_layout.size_of_int(16), crystal_layout.align_of_int(16) | |
agg.add crystal_layout.size_of_int(64), crystal_layout.align_of_int(64) | |
agg.add crystal_layout.size_of_int(8), crystal_layout.align_of_int(8) | |
agg.alignment.should eq llvm_layout.abi_alignment(ctx.struct([ | |
ctx.int8, | |
ctx.int32, | |
ctx.float, | |
ctx.int16, | |
ctx.int64, | |
ctx.int8, | |
] of LLVM::Type)) * 8 | |
end | |
end | |
it "packed struct" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate(packed: true) | |
agg.add crystal_layout.size_of_int(8), crystal_layout.align_of_int(8) | |
agg.add crystal_layout.size_of_int(32), crystal_layout.align_of_int(32) | |
agg.add crystal_layout.size_of_int(16), crystal_layout.align_of_int(16) | |
agg.alignment.should eq llvm_layout.abi_alignment(ctx.struct([ | |
ctx.int8, | |
ctx.int32, | |
ctx.int16, | |
] of LLVM::Type, packed: true)) * 8 | |
end | |
end | |
end | |
end | |
describe "size_of" do | |
it "integers" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
[1, 7, 8, 15, 16, 24, 32, 64, 65, 128, 256].each do |n| | |
crystal_layout.size_of_int(n).should eq llvm_layout.abi_size(ctx.int(n)) * 8 | |
end | |
end | |
end | |
it "floats" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
crystal_layout.size_of_float(16).should eq llvm_layout.abi_size(ctx.half) * 8 | |
crystal_layout.size_of_float(32).should eq llvm_layout.abi_size(ctx.float) * 8 | |
crystal_layout.size_of_float(64).should eq llvm_layout.abi_size(ctx.double) * 8 | |
{% if triple.starts_with?("x86_64") || triple =~ /^i[3456]?86/ %} | |
crystal_layout.size_of_float(80).should eq llvm_layout.abi_size(ctx.x86_fp80) * 8 | |
{% end %} | |
crystal_layout.size_of_float(128).should eq llvm_layout.abi_size(ctx.fp128) * 8 | |
end | |
end | |
describe "pointer" do | |
it "default address space (0)" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
crystal_layout.size_of_pointer.should eq llvm_layout.abi_size(ctx.void_pointer) * 8 | |
end | |
end | |
it "specific address spaces" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
crystal_layout.pointer_alignments.each do |x| | |
align = crystal_layout.size_of_pointer(x[:address_space]) | |
align.should eq llvm_layout.abi_size(ctx.void_pointer(x[:address_space])) * 8 | |
end | |
end | |
end | |
end | |
describe "aggregate" do | |
it "empty struct" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate | |
agg.size.should eq llvm_layout.abi_size(ctx.struct([] of LLVM::Type)) * 8 | |
end | |
end | |
it "one property" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate | |
agg.add crystal_layout.size_of_int(32), crystal_layout.align_of_int(32) | |
agg.size.should eq llvm_layout.abi_size(ctx.struct([ctx.int32] of LLVM::Type)) * 8 | |
end | |
end | |
it "a few properties" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate | |
agg.add crystal_layout.size_of_int(8), crystal_layout.align_of_int(8) | |
agg.add crystal_layout.size_of_int(32), crystal_layout.align_of_int(32) | |
agg.add crystal_layout.size_of_int(16), crystal_layout.align_of_int(16) | |
agg.size.should eq llvm_layout.abi_size(ctx.struct([ | |
ctx.int8, | |
ctx.int32, | |
ctx.int16, | |
] of LLVM::Type)) * 8 | |
end | |
end | |
it "large properties" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate | |
agg.add crystal_layout.size_of_int(8), crystal_layout.align_of_int(8) | |
agg.add crystal_layout.size_of_int(512), crystal_layout.align_of_int(512) | |
agg.size.should eq llvm_layout.abi_size(ctx.struct([ | |
ctx.int8, | |
ctx.int(512), | |
] of LLVM::Type)) * 8 | |
end | |
end | |
it "many properties" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate | |
agg.add crystal_layout.size_of_int(8), crystal_layout.align_of_int(8) | |
agg.add crystal_layout.size_of_int(32), crystal_layout.align_of_int(32) | |
agg.add crystal_layout.size_of_float(32),crystal_layout.align_of_float(32) | |
agg.add crystal_layout.size_of_int(16), crystal_layout.align_of_int(16) | |
agg.add crystal_layout.size_of_int(64), crystal_layout.align_of_int(64) | |
agg.add crystal_layout.size_of_int(8), crystal_layout.align_of_int(8) | |
agg.size.should eq llvm_layout.abi_size(ctx.struct([ | |
ctx.int8, | |
ctx.int32, | |
ctx.float, | |
ctx.int16, | |
ctx.int64, | |
ctx.int8, | |
] of LLVM::Type)) * 8 | |
end | |
end | |
it "packed struct" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate(packed: true) | |
agg.add crystal_layout.size_of_int(8), crystal_layout.align_of_int(8) | |
agg.add crystal_layout.size_of_int(32), crystal_layout.align_of_int(32) | |
agg.add crystal_layout.size_of_int(16), crystal_layout.align_of_int(16) | |
agg.size.should eq llvm_layout.abi_size(ctx.struct([ | |
ctx.int8, | |
ctx.int32, | |
ctx.int16, | |
] of LLVM::Type, packed: true)) * 8 | |
end | |
end | |
end | |
end | |
describe "offset_of" do | |
it "empty struct" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate | |
expect_raises(IndexError) { agg.offset(0) } | |
end | |
end | |
it "one property" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate | |
agg.add crystal_layout.size_of_int(32), crystal_layout.align_of_int(32) | |
ty = ctx.struct([ | |
ctx.int32, | |
] of LLVM::Type) | |
agg.offset(0).should eq llvm_layout.offset_of_element(ty, 0) * 8 | |
end | |
end | |
it "a few properties" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate | |
agg.add crystal_layout.size_of_int(8), crystal_layout.align_of_int(8) | |
agg.add crystal_layout.size_of_int(32), crystal_layout.align_of_int(32) | |
agg.add crystal_layout.size_of_int(16), crystal_layout.align_of_int(16) | |
ty = ctx.struct([ | |
ctx.int8, | |
ctx.int32, | |
ctx.int16, | |
] of LLVM::Type) | |
3_u32.times { |i| agg.offset(i).should eq llvm_layout.offset_of_element(ty, i) * 8 } | |
end | |
end | |
it "large properties" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate | |
agg.add crystal_layout.size_of_int(8), crystal_layout.align_of_int(8) | |
agg.add crystal_layout.size_of_int(512), crystal_layout.align_of_int(512) | |
ty = ctx.struct([ | |
ctx.int8, | |
ctx.int(512), | |
] of LLVM::Type) | |
2_u32.times { |i| agg.offset(i).should eq llvm_layout.offset_of_element(ty, i) * 8 } | |
end | |
end | |
it "many properties" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate | |
agg.add crystal_layout.size_of_int(8), crystal_layout.align_of_int(8) | |
agg.add crystal_layout.size_of_int(32), crystal_layout.align_of_int(32) | |
agg.add crystal_layout.size_of_float(32),crystal_layout.align_of_float(32) | |
agg.add crystal_layout.size_of_int(16), crystal_layout.align_of_int(16) | |
agg.add crystal_layout.size_of_int(64), crystal_layout.align_of_int(64) | |
agg.add crystal_layout.size_of_int(8), crystal_layout.align_of_int(8) | |
ty = ctx.struct([ | |
ctx.int8, | |
ctx.int32, | |
ctx.float, | |
ctx.int16, | |
ctx.int64, | |
ctx.int8, | |
] of LLVM::Type) | |
6_u32.times { |i| agg.offset(i).should eq llvm_layout.offset_of_element(ty, i) * 8 } | |
end | |
end | |
it "packed struct" do | |
machine_for({{triple}}) do |ctx, llvm_layout, crystal_layout| | |
agg = crystal_layout.aggregate(packed: true) | |
agg.add crystal_layout.size_of_int(8), crystal_layout.align_of_int(8) | |
agg.add crystal_layout.size_of_int(32), crystal_layout.align_of_int(32) | |
agg.add crystal_layout.size_of_int(16), crystal_layout.align_of_int(16) | |
ty = ctx.struct([ | |
ctx.int8, | |
ctx.int32, | |
ctx.int16, | |
] of LLVM::Type, packed: true) | |
3_u32.times { |i| agg.offset(i).should eq llvm_layout.offset_of_element(ty, i) * 8 } | |
end | |
end | |
end | |
end | |
{% end %} | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment