[LLVMdev] alignment checking in isSafeToEliminateVarargsCast

Eli Friedman eli.friedman at gmail.com
Fri Jul 29 12:05:36 PDT 2011


On Fri, Jul 29, 2011 at 11:13 AM, Akira Hatanaka <ahatanak at gmail.com> wrote:
> I have a question about a problem I came across while I was adding support
> for aggregate va_arg expression in clang.
> The following is the example program I will use in this email. I compile the
> program with clang targeting mips. Note that I have not pushed all the
> changes I have made yet, so you will not be able to see the same results.
>
> $ clang  -ccc-host-triple mipsel-unknown-linux -ccc-clang-archs mipsel foo.c
> -o foo.ll  -emit-llvm -O3 -S
> // foo.c
> // adapted from test-suite/SingleSource/UnitTests/2003-05-07-VarArg
> s.c
> #include <stdarg.h>
>
> typedef struct DWordS_struct    { int i; char c; } DWordS;
> typedef struct LargeS_struct { int i; double d; DWordS* ptr; int j; }
> LargeS;
>
> void test(char *fmt,...);
>
> int main() {
>   DWordS dw = { 18, 'a' };
>   LargeS ls = { 21, 22.0, &dw, 23 };
>
>   test("DQL", dw, ls);
>
>   return 0;
> }
>
> After code generation, clang runs several llvm IR optimization passes
> including InstCombine.
> The following is the bitcode before InstCombine is run. The second argument
> %dw of function test is of type "%struct.DWordS_struct* byval", which has a
> 4-byte alignment.
>
>
> Breakpoint 1 at
> llvm/lib/Transforms/InstCombine/InstructionCombining.cpp:1652
> 1652                   InstCombineIRInserter(Worklist));
> (gdb) p F.dump()
>
> define i32 @main() nounwind {
> entry:
>   %dw = alloca %struct.DWordS_struct, align 4
>   %ls = alloca %struct.LargeS_struct, align 8
>   %tmp = bitcast %struct.DWordS_struct* %dw to i8*
>   call void @llvm.memcpy.p0i8.p0i8.i32(i8* %tmp, i8* bitcast ({ i32, i8, [3
> x i8] }* @main.dw to i8*), i32 8, i32 4, i1 false)
>   %i = getelementptr inbounds %struct.LargeS_struct* %ls, i32 0, i32 0
>   store i32 21, i32* %i, align 4, !tbaa !0
>   %d = getelementptr inbounds %struct.LargeS_struct* %ls, i32 0, i32 1
>   store double 2.200000e+01, double* %d, align 8, !tbaa !3
>   %ptr = getelementptr inbounds %struct.LargeS_struct* %ls, i32 0, i32 2
>   store %struct.DWordS_struct* %dw, %struct.DWordS_struct** %ptr, align 4,
> !tbaa !4
>   %j = getelementptr inbounds %struct.LargeS_struct* %ls, i32 0, i32 3
>   store i32 23, i32* %j, align 4, !tbaa !0
>   call void (i8*, ...)* @test(i8* getelementptr inbounds ([4 x i8]* @.str,
> i32 0, i32 0), %struct.DWordS_struct* byval %dw, %struct.LargeS_struct*
> byval %ls)
>   ret i32 0
> }
>
>
> After InstCombine is run, the type of the second argument of function test
> has been changed to "i64* byval", which has an 8-byte alignment. This
> becomes a problem if the target is mips-o32, since a function call uses
> different registers or different stack locations to pass arguments depending
> on the alignment of the type of the argument that is passed.
>
> Breakpoint 2 at
> llvm/lib/Transforms/InstCombine/InstructionCombining.cpp:1667
> 1667      return EverMadeChange;
> (gdb) p F.dump()
> define i32 @main() nounwind {
> entry:
>   %dw = alloca i64, align 8
>   %tmpcast = bitcast i64* %dw to %struct.DWordS_struct*
>   %ls = alloca %struct.LargeS_struct, align 8
>   store i64 416611827730, i64* %dw, align 8
>   %i = getelementptr inbounds %struct.LargeS_struct* %ls, i32 0, i32 0
>   store i32 21, i32* %i, align 8, !tbaa !0
>   %d = getelementptr inbounds %struct.LargeS_struct* %ls, i32 0, i32 1
>   store double 2.200000e+01, double* %d, align 8, !tbaa !3
>   %ptr = getelementptr inbounds %struct.LargeS_struct* %ls, i32 0, i32 2
>   store %struct.DWordS_struct* %tmpcast, %struct.DWordS_struct** %ptr, align
> 8, !tbaa !4
>   %j = getelementptr inbounds %struct.LargeS_struct* %ls, i32 0, i32 3
>   store i32 23, i32* %j, align 4, !tbaa !0
>   call void (i8*, ...)* @test(i8* getelementptr inbounds ([4 x i8]* @.str,
> i32 0, i32 0), i64* byval %dw, %struct.LargeS_struct* byval %ls) nounwind
>   ret i32 0
> }
>
> This transformation seems to take place in InstCombiner::visitCallSite in
> InstCombineCalls.cpp, which calls function isSafeToEliminateVarargsCast to
> decide whether it is safe to eliminate a cast instruction and replace the
> argument that is passed. Would it be possible to add code in
> isSafeToEliminateVarargsCast that checks alignments of SrcTy and DstTy and
> returns false if they are not the same? Or are there better ways to
> circumvent this problem?

Looks like a legitimate bug.  We really shouldn't be touching the
types of byval arguments...

-Eli




More information about the llvm-dev mailing list