aaron.ballman added a reviewer: majnemer.
aaron.ballman added a comment.

> I *think* the problem is that we gin up the function type for a 
> non-prototyped function based on the function call expression argument types, 
> and the literal `0` is getting the type `signed long long`.

I think this is because of `CodeGenFunction::getVarArgType()` and is specific 
to the Windows ABI.

  // System headers on Windows define NULL to 0 instead of 0LL on Win64. MSVC
  // implicitly widens null pointer constants that are arguments to varargs
  // functions to pointer-sized ints.

This causes the 0 to be emit as a 64-bit value rather than a 32-bit value. 
Indeed, if you change your original example to pass `1` rather than `0` when 
calling `p`, you no longer get the UB:

  ; Function Attrs: noinline nounwind
  define void @f() #0 {
  entry:
    %0 = load void (...)*, void (...)** @p, align 8
    %callee.knr.cast = bitcast void (...)* %0 to void (i32)*
    call void %callee.knr.cast(i32 1)
    ret void
  }

However, when I test with MSVC 2015, I do not get the behavior that Clang 
produces. My test was:

  void h(int i, ...) {}
  void i(int i, int j, int k, int l, int m) {}
  
  void(*p)() = i;
  
  void f() {
    p(0, 1, 2, 3, 0);
    h(0, 1, 2, 3, 0);
    i(0, 1, 2, 3, 0);
  }

MSVC outputs:

  ; 8    :   p(0, 1, 2, 3, 0);
  
        mov     DWORD PTR [rsp+32], 0
        mov     r9d, 3
        mov     r8d, 2
        mov     edx, 1
        xor     ecx, ecx
        call    QWORD PTR p
  
  ; 9    :   h(0, 1, 2, 3, 0);
  
        mov     QWORD PTR [rsp+32], 0
        mov     r9d, 3
        mov     r8d, 2
        mov     edx, 1
        xor     ecx, ecx
        call    h
  
  ; 10   :   i(0, 1, 2, 3, 0);
  
        mov     DWORD PTR [rsp+32], 0
        mov     r9d, 3
        mov     r8d, 2
        mov     edx, 1
        xor     ecx, ecx
        call    i

Clang outputs:

  %callee.knr.cast = bitcast void (...)* %0 to void (i64, i32, i32, i32, i64)*
  call void %callee.knr.cast(i64 0, i32 1, i32 2, i32 3, i64 0)
  call void (i32, ...) @h(i32 0, i32 1, i32 2, i32 3, i64 0)
  call void @i(i32 0, i32 1, i32 2, i32 3, i32 0)

Note that the K&R call casts to i64 in Clang but uses a DWORD PTR in MSVC. Only 
the variadic call to `h()` uses the QWORD PTR. So I think the correct behavior 
is to only enable the vararg behavior when the function is variadic with an 
ellipsis rather than variadic due to a lack of prototype.

Thoughts?


https://reviews.llvm.org/D28166



_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to