llvm / llvm/llvm-project

LLVM IR va_arg instruction on Windows x86_64 uses wrong stride for arguments

Open
#206,824 3 comments 0 reactions 0 assignees View on GitHub
llvm:ir question
Dominant language
LLVM
Stars
40.5k
Forks
18.7k
PR merge metrics
PR metrics pending

Description

Hi everyone! While testing for [this issue](https://github.com/odin-lang/Odin/issues/6911), when I compiled and ran the program on Windows, I noticed inconsistent outputs from when I ran it on Linux.

I looked into it more and I found out that the ``va_arg`` instruction will use a stride of the primitive type for Windows instead of using the stride of a register's size if the primitive type's size is smaller.

Below is a minimum reproducible case program written in LLVM IR (``example.ll``)
```llvm
@str = constant [10 x i8] c"%d %d %d\0A\00"

; This struct is different for every platform. For most platforms,
; it is merely a ptr.
%struct.va_list = type { ptr }

; For Unix x86_64 platforms, va_list is the following struct:
; %struct.va_list = type { i32, i32, ptr, ptr }

define i32 @test(...) {
; Initialize variable argument processing
%ap = alloca %struct.va_list
call void @llvm.va_start.p0(ptr %ap)

; Read a single integer argument
%tmp1 = va_arg ptr %ap, i8
%tmp2 = va_arg ptr %ap, i8
%tmp3 = va_arg ptr %ap, i8
%tmp4 = va_arg ptr %ap, i8
%tmp5 = va_arg ptr %ap, i8
%tmp6 = va_arg ptr %ap, i8
%tmp7 = va_arg ptr %ap, i8
%tmp8 = va_arg ptr %ap, i8
%tmp9 = va_arg ptr %ap, i8
call i32 @printf(ptr @str, i8 %tmp1, i8 %tmp5, i8 %tmp9)

; Stop processing of arguments.
call void @llvm.va_end.p0(ptr %ap)
ret i32 0
}
define i32 @main() {
call i32 @test(i8 1, i8 2, i8 3, i8 4, i8 5, i8 6, i8 7, i8 8, i8 9)
ret i32 0
}

declare void @llvm.va_start.p0(ptr)
declare void @llvm.va_copy.p0(ptr, ptr)
declare void @llvm.va_end.p0(ptr)
declare void @printf(ptr, ...)
```

On Windows, compile with ``clang example.ll`` and run ``a.exe`` to get ``1 0 2``
Here is an example report and run from my last compilation and execution
```
$ ~/Downloads/clang+llvm-22.1.8-x86_64-pc-windows-msvc/bin/clang.exe --version
clang version 22.1.8 (https://github.com/llvm/llvm-project ca7933e47d3a3451d81e72ac174dcb5aa28b59d1)
Target: x86_64-pc-windows-msvc
Thread model: posix
InstalledDir: C:\Users\REDACTED\Downloads\clang+llvm-22.1.8-x86_64-pc-windows-msvc\bin

$ ~/Downloads/clang+llvm-22.1.8-x86_64-pc-windows-msvc/bin/clang.exe example.ll
warning: overriding the module target triple with x86_64-pc-windows-msvc19.51.36248 [-Woverride-module]
1 warning generated.

$ ./a.exe
1 0 2
```

Contributor guide

Open the contributing guide

Research direction

Start by compiling example.ll with clang on Windows and running a.exe to reproduce the 1 0 2 output. Trace how the va_arg instruction handles successive i8 arguments on the x86_64 Windows target, then verify the result against the expected argument sequence and the Linux behavior.

Written by the indexing model from the issue text.

Assessment

Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
50/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.