Skip to content

Commit a374dfc

Browse files
authored
Fix System V ABI for arrays of packed structs with misaligned fields (#16314)
Avoid compiler crash with `UInt8[N]` external variable.
1 parent 7572ca2 commit a374dfc

File tree

3 files changed

+102
-14
lines changed

3 files changed

+102
-14
lines changed

spec/compiler/codegen/abi/x86_64_spec.cr

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,50 @@ class Crystal::ABI
200200
info.arg_types[0].should eq(ArgType.direct(str, cast: ctx.struct([ctx.int64])))
201201
info.return_type.should eq(ArgType.direct(str, cast: ctx.struct([ctx.int64])))
202202
end
203+
204+
test "does with array of packed struct containing unaligned fields (#16312)" do |abi, ctx|
205+
str = ctx.struct([ctx.int8, ctx.int16], packed: true).array(1)
206+
arg_types = [str]
207+
return_type = str
208+
209+
info = abi.abi_info(arg_types, return_type, true, ctx)
210+
info.arg_types.size.should eq(1)
211+
212+
info.arg_types[0].should eq(ArgType.indirect(str, LLVM::Attribute::ByVal))
213+
info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))
214+
215+
str = ctx.struct([ctx.int8, ctx.int16], packed: true).array(2)
216+
arg_types = [str]
217+
return_type = str
218+
219+
info = abi.abi_info(arg_types, return_type, true, ctx)
220+
info.arg_types.size.should eq(1)
221+
222+
info.arg_types[0].should eq(ArgType.indirect(str, LLVM::Attribute::ByVal))
223+
info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))
224+
225+
str = ctx.struct([ctx.int16, ctx.int8], packed: true).array(2)
226+
arg_types = [str]
227+
return_type = str
228+
229+
info = abi.abi_info(arg_types, return_type, true, ctx)
230+
info.arg_types.size.should eq(1)
231+
232+
info.arg_types[0].should eq(ArgType.indirect(str, LLVM::Attribute::ByVal))
233+
info.return_type.should eq(ArgType.indirect(str, LLVM::Attribute::StructRet))
234+
end
235+
236+
test "does with array of packed struct not containing unaligned fields (#16312)" do |abi, ctx|
237+
str = ctx.struct([ctx.int16, ctx.int8], packed: true).array(1)
238+
arg_types = [str]
239+
return_type = str
240+
241+
info = abi.abi_info(arg_types, return_type, true, ctx)
242+
info.arg_types.size.should eq(1)
243+
244+
info.arg_types[0].should eq(ArgType.direct(str, cast: ctx.struct([ctx.int64])))
245+
info.return_type.should eq(ArgType.direct(str, cast: ctx.struct([ctx.int64])))
246+
end
203247
end
204248
{% end %}
205249
end

src/compiler/crystal/codegen/abi/x86_64.cr

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -275,14 +275,36 @@ class Crystal::ABI::X86_64 < Crystal::ABI
275275
size(type, 8)
276276
end
277277

278-
def has_misaligned_fields?(type : LLVM::Type) : Bool
279-
return false unless type.packed_struct?
280-
offset = 0
281-
type.struct_element_types.each do |elem|
282-
return true unless offset.divisible_by?(align(elem))
283-
offset += size(elem)
278+
def has_misaligned_fields?(type : LLVM::Type, offset : Int = 0) : Bool
279+
case type.kind
280+
when LLVM::Type::Kind::Struct
281+
return false unless type.packed_struct?
282+
type.struct_element_types.each do |elem|
283+
return true unless offset.divisible_by?(align(elem))
284+
offset += size(elem)
285+
end
286+
false
287+
when LLVM::Type::Kind::Array
288+
# Given:
289+
#
290+
# ```
291+
# @[Packed]
292+
# struct Foo
293+
# x : Int16
294+
# y : Int8
295+
# end
296+
# ```
297+
#
298+
# the types `Foo` and `Foo[1]` have no misaligned fields, but `Foo[2]`
299+
# does, because the field `.[1].x` has offset 3 and a natural alignment of
300+
# 2. Checking for the first two elements is sufficient; if both contain no
301+
# misaligned fields, then `size(elem) % align(elem) == 0` must be true,
302+
# meaning array indices have no effect on element alignment.
303+
elem = type.element_type
304+
has_misaligned_fields?(elem) || type.array_size > 1 && has_misaligned_fields?(elem, size(elem))
305+
else
306+
false
284307
end
285-
false
286308
end
287309

288310
enum RegClass

src/llvm/abi/x86_64.cr

Lines changed: 29 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -276,14 +276,36 @@ class LLVM::ABI::X86_64 < LLVM::ABI
276276
size(type, 8)
277277
end
278278

279-
def has_misaligned_fields?(type : Type) : Bool
280-
return false unless type.packed_struct?
281-
offset = 0
282-
type.struct_element_types.each do |elem|
283-
return true unless offset.divisible_by?(align(elem))
284-
offset += size(elem)
279+
def has_misaligned_fields?(type : Type, offset : Int = 0) : Bool
280+
case type.kind
281+
when Type::Kind::Struct
282+
return false unless type.packed_struct?
283+
type.struct_element_types.each do |elem|
284+
return true unless offset.divisible_by?(align(elem))
285+
offset += size(elem)
286+
end
287+
false
288+
when Type::Kind::Array
289+
# Given:
290+
#
291+
# ```
292+
# @[Packed]
293+
# struct Foo
294+
# x : Int16
295+
# y : Int8
296+
# end
297+
# ```
298+
#
299+
# the types `Foo` and `Foo[1]` have no misaligned fields, but `Foo[2]`
300+
# does, because the field `.[1].x` has offset 3 and a natural alignment of
301+
# 2. Checking for the first two elements is sufficient; if both contain no
302+
# misaligned fields, then `size(elem) % align(elem) == 0` must be true,
303+
# meaning array indices have no effect on element alignment.
304+
elem = type.element_type
305+
has_misaligned_fields?(elem) || type.array_size > 1 && has_misaligned_fields?(elem, size(elem))
306+
else
307+
false
285308
end
286-
false
287309
end
288310

289311
enum RegClass

0 commit comments

Comments
 (0)