Skip to content

[AVR] alignment of stack allocations gets silently ignored #169190

@meithecatte

Description

@meithecatte

Consider the following translation unit:

struct nya {
        int a;
};

void g(void *);

void f() {
        _Alignas(64)
        struct nya x = {3};
        g(&x);
}

When compiled with clang as follows, the generated code doesn't make any effort to actually satisfy the alignment constaint:

clang --target=avr-none -mmcu=atmega2560 -c test.c -o test-clang.o
LLVM IR generated by clang
; ModuleID = 'test.c'
source_filename = "test.c"
target datalayout = "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8"
target triple = "avr-none"

%struct.nya = type { i16 }

@__const.f.x = private unnamed_addr constant %struct.nya { i16 3 }, align 64

; Function Attrs: noinline nounwind optnone
define dso_local void @f() addrspace(1) #0 {
  %1 = alloca %struct.nya, align 64
  call addrspace(1) void @llvm.memcpy.p0.p0.i16(ptr align 64 %1, ptr align 64 @__const.f.x, i16 2, i1 false)
  call addrspace(1) void @g(ptr noundef %1)
  ret void
}

; Function Attrs: nocallback nofree nounwind willreturn memory(argmem: readwrite)
declare void @llvm.memcpy.p0.p0.i16(ptr noalias writeonly captures(none), ptr noalias readonly captures(none), i16, i1 immarg) addrspace(1) #1

declare dso_local void @g(ptr noundef) addrspace(1) #2

attributes #0 = { noinline nounwind optnone "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="atmega2560" }
attributes #1 = { nocallback nofree nounwind willreturn memory(argmem: readwrite) }
attributes #2 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="atmega2560" }

!llvm.module.flags = !{!0, !1}
!llvm.ident = !{!2}

!0 = !{i32 1, !"wchar_size", i32 2}
!1 = !{i32 7, !"frame-pointer", i32 2}
!2 = !{!"clang version 21.1.6"}
Assembly generated by LLVM
f:                                      ; @f
; %bb.0:
	push	r28
	push	r29
	in	r28, 61
	in	r29, 62
	sbiw	r28, 60
	in	r0, 63
	cli
	out	62, r29
	out	63, r0
	out	61, r28
	lds	r24, __const.f.x
	lds	r25, __const.f.x+1
	std	Y+2, r25
	std	Y+1, r24
	movw	r24, r28
	adiw	r24, 1
	call	g
	adiw	r28, 60
	in	r0, 63
	cli
	out	62, r29
	out	63, r0
	out	61, r28
	pop	r29
	pop	r28
	ret

By comparison, GCC properly aligns the stack allocation (by allocating alignment - 1 + size bytes and positioning the actual object at a variable stack frame offset):

avr-gcc -mmcu=atmega2560 -c test.c -o test-gcc.o
Assembly generated by GCC
f:
	push r28
	push r29
	in r28,__SP_L__
	in r29,__SP_H__
	subi r28,65
	sbc r29,__zero_reg__
	in __tmp_reg__,__SREG__
	cli
	out __SP_H__,r29
	out __SREG__,__tmp_reg__
	out __SP_L__,r28
/* prologue: function */
/* frame size = 65 */
/* stack size = 67 */
.L__stack_usage = 67
	movw r24,r28
	adiw r24,1
	adiw r24,63
	clr __tmp_reg__
	lsl r24
	rol r25
	rol __tmp_reg__
	lsl r24
	rol r25
	rol __tmp_reg__
	mov r24,r25
	mov r25,__tmp_reg__
	clr __tmp_reg__
	lsr r25
	ror r24
	ror __tmp_reg__
	lsr r25
	ror r24
	ror __tmp_reg__
	mov r25,r24
	mov r24,__tmp_reg__
	ldi r18,lo8(3)
	ldi r19,0
	movw r30,r24
	std Z+1,r19
	st Z,r18
	call g
	nop
/* epilogue start */
	subi r28,-65
	sbci r29,-1
	in __tmp_reg__,__SREG__
	cli
	out __SP_H__,r29
	out __SREG__,__tmp_reg__
	out __SP_L__,r28
	pop r29
	pop r28
	ret

Now, you might ask: "hold on, there's nothing on the AVR that'd be affected by memory alignment, why do you care about this". Well, recently the Rust formatting machinery has started making use of pointer tagging, which breaks spectacularly when LLVM ignores the alignment that rustc requests for some stack-allocated structs: rust-lang/rust#149223

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions