naked assembler / function written entirely in asm

Hi everybody.

I’m having (yet) another look at trying to get naked functions from D (1) working in our LLVM D Compiler - LDC (2).

I have this test case:

/// D CODE
///
extern(C) int printf(char*, …);

ulong retval() {
asm { naked; mov EAX, 0xff; mov EDX, 0xaa; ret; }
}

ulong retval2() {
return (cast(ulong)0xaa << 32) | 0xff;
}

void main() {
printf("%llu\n%llu\n", retval(), retval2());
}
///
///

I’ve tried out a few things. This currently compiles to:

///
///
target datalayout = “e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32”
target triple = “i686-pc-linux-gnu”

@.str1 = internal constant [11 x i8] c"%llu\0A%llu\0A\00" ; <[11 x i8]*> [#uses=1]

declare i32 @printf(i8*, …)

define x86_stdcallcc i64 @_D3bar6retvalFZm() noinline {
entry:
call void asm sideeffect "movl $0, %eax ; movl $1, %edx ; ret ", “i,i,~{eax},~{edx}”(i32 255, i32 170)
unreachable
}

define x86_stdcallcc i64 @_D3bar7retval2FZm() {
entry:
ret i64 730144440575
}

define x86_stdcallcc i32 @_Dmain({ i32, { i32, i8* }* } %unnamed) {
entry:
%tmp = call x86_stdcallcc i64 @_D3bar6retvalFZm() ; [#uses=1]
%tmp1 = call x86_stdcallcc i64 @_D3bar7retval2FZm() ; [#uses=1]
%tmp2 = call i32 (i8*, …)* @printf(i8* getelementptr ([11 x i8]* @.str1, i32 0, i32 0), i64 %tmp, i64 %tmp1) ; [#uses=0]
ret i32 0
}
///
///

When this is linked as an application, without being optimized, it works as expected.
But when optimized, _Dmain is reduced to:

///
///
define x86_stdcallcc i32 @_Dmain({ i32, { i32, i8* }* } %unnamed) noreturn nounwind {
entry:
%tmp = call x86_stdcallcc i64 @_D3bar6retvalFZm() ; [#uses=0]
unreachable
}
///
///

If the first impl is changed to:

///
///
define x86_stdcallcc i64 @_D3bar6retvalFZm() noinline {
entry:
call void asm sideeffect "movl $0, %eax ; movl $1, %edx ; ret ", “i,i,~{eax},~{edx}”(i32 255, i32 170)
ret i64 undef ;
}
///
///

It still works when not optimized. However, when optimized, then _Dmain becomes:

///
///
define x86_stdcallcc i32 @_Dmain({ i32, { i32, i8* }* } %unnamed) {
entry:
%tmp = call x86_stdcallcc i64 @_D3bar6retvalFZm() ; [#uses=0]
%tmp1 = call x86_stdcallcc i64 @_D3bar7retval2FZm() ; [#uses=0]
%tmp2 = call i32 (i8*, …)* @printf(i8* getelementptr ([11 x i8]* @.str1, i32 0, i32 0), i64 undef, i64 730144440575) ; [#uses=0]
ret i32 0
}
///
///

which (as expected) prints a bogus value.

I realize I’m entering some undefined/invalid territory here, but I’d really like to get some ideas for what I could do to make this D code work.
Ideally I would like a naked attribute for a function which would mean that the entire function is composed of a single asm block.

Module level assembler could probably be used for this, but it would be nice if we could get away with using a single asm translator in the compiler.

Thanx in advance,

  • Tomas Lindquist Olsen

I really do think that module-level asm is the right solution here. Unfortunately, the semantics of naked functions are very touchy, and you don't want (for example) the inliner to try inlining these things.

-Chris