llvm / llvm/llvm-project

[m68k] Miscompilation: getelementptr generates incorrect assembly in some circumstances

Open
#200,984 3 comments 0 reactions 0 assignees View on GitHub
backend:m68k miscompilation
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

Continuing on my journey to make Rust run on my Atari ST (see #200826).

New bug:

```rs
#![no_std]
#[inline(never)]
pub unsafe fn repro_raw_buf_index_bug() {
let mut buf = [0u8; 10];
let bp = buf.as_ptr() as u32;

let mut i = core::hint::black_box(1);

let ptr = &raw const buf[i];

console_write_char(b'X');
console_write_char(bp as u8);
console_write_char(ptr as u8);
}

unsafe extern "C" {
fn console_write_char(x: u8);
}
```
This should print X, then (the ASCII char corresponding to) the last byte of `buf`'s address, then that byte plus one.

Here is a (not that minimal) IR:
```llvm
define void @repro_raw_buf_index_bug() {
start:
%i = alloca [4 x i8], align 4
%buf = alloca [10 x i8], align 1
store i32 1, ptr %i, align 4
call void asm sideeffect "", "r,~{memory}"(ptr nonnull %i)
%0 = load i32, ptr %i, align 4
%_8 = icmp ult i32 %0, 10
br i1 %_8, label %bb1, label %panic

bb1:
%1 = ptrtoint ptr %buf to i32
%ptr = getelementptr inbounds nuw i8, ptr %buf, i32 %0
call void @console_write_char(i8 noundef zeroext 88)
%_11 = trunc i32 %1 to i8
call void @console_write_char(i8 noundef zeroext %_11)
%2 = ptrtoint ptr %ptr to i32
%_13 = trunc i32 %2 to i8
call void @console_write_char(i8 noundef zeroext %_13)
ret void

panic:
call void @panic()
unreachable
}

declare void @console_write_char(i8 noundef zeroext)

declare void @panic()
```
Latest master and https://github.com/glaubitz/llvm-project/commits/m68k-reloc-devel-new both generate:
```asm
repro_raw_buf_index_bug: ; @repro_raw_buf_index_bug
.cfi_startproc
; %bb.0: ; %start
suba.l #28, %sp
.cfi_def_cfa_offset -32
movem.l %d2, (24,%sp) ; 8-byte Folded Spill
move.l #1, (20,%sp)
lea (20,%sp), %a0
move.l %a0, %d0
;APP
;NO_APP
move.l (20,%sp), %d0
sub.l #9, %d0
bhi .LBB0_2
; %bb.1: ; %bb1
move.l #88, (%sp)
jsr console_write_char
lea (10,%sp), %a0
move.l %a0, %d2
and.l #255, %d2
move.l %d2, (%sp) ; notice how both calls receive the same parameter (d2), and no addition is made anywhere
jsr console_write_char
move.l %d2, (%sp)
jsr console_write_char
movem.l (24,%sp), %d2 ; 8-byte Folded Reload
adda.l #28, %sp
rts
.LBB0_2: ; %panic
jsr panic
```

However, removing the bounds check suppresses the bug:
```llvm
define void @repro_raw_buf_index_bug() {
start:
%i = alloca [4 x i8], align 4
%buf = alloca [10 x i8], align 1
store i32 1, ptr %i, align 4
call void asm sideeffect "", "r,~{memory}"(ptr nonnull %i)
%0 = load i32, ptr %i, align 4
%1 = ptrtoint ptr %buf to i32
%ptr = getelementptr inbounds nuw i8, ptr %buf, i32 %0
call void @console_write_char(i8 noundef zeroext 88)
%_11 = trunc i32 %1 to i8
call void @console_write_char(i8 noundef zeroext %_11)
%2 = ptrtoint ptr %ptr to i32
%_13 = trunc i32 %2 to i8
call void @console_write_char(i8 noundef zeroext %_13)
ret void
}
```
gives
```asm
repro_raw_buf_index_bug: ; @repro_raw_buf_index_bug
.cfi_startproc
; %bb.0: ; %start
suba.l #28, %sp
.cfi_def_cfa_offset -32
movem.l %d2-%d3, (20,%sp) ; 12-byte Folded Spill
move.l #1, (16,%sp)
lea (16,%sp), %a0
move.l %a0, %d0
;APP
;NO_APP
lea (6,%sp), %a0
move.l %a0, %d3
move.l %d3, %d2
add.l (16,%sp), %d2 ; notice this "add" which is completely missing from the previous asm
move.l #88, (%sp)
jsr console_write_char
and.l #255, %d3
move.l %d3, (%sp)
jsr console_write_char
and.l #255, %d2
move.l %d2, (%sp)
jsr console_write_char
movem.l (20,%sp), %d2-%d3 ; 12-byte Folded Reload
adda.l #28, %sp
rts
```

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.