In ISel, where Constant<0> comes from?

Hello, LLVM devs.

I’m compiling the following simple IR:

define dso_local i32 @main(i32 %argc, i8** %argv) {
entry:
%retval = alloca i32, align 4
%argc.addr = alloca i32, align 4
%argv.addr = alloca i8**, align 8
store i32 0, i32* %retval, align 4
store i32 %argc, i32* %argc.addr, align 4
store i8** %argv, i8*** %argv.addr, align 8
ret i32 0
}

using llc -march=sparc -debug-only=isel.

In the Initial selection DAG I see

t19: ch,glue = CopyToReg t17, Register:i32 $i0, Constant:i32<0>

line. The same “Constant:i32<0>” node I see in my toy backend, which forces me to add a pattern that lowers it using “xor reg,reg”. Much like “or g0,g0” pattern in SPARC.

However, I don’t see that Constant node when compiling using X86 backend. How does it achieve this? And why initial DAGs are different at all? I got impression that initial DAG is fully target-independent, so these DAG should be the same before starting ISel. Am I wrong?

Thanks in advance.

The selection DAG is very much target-specific. The differences in the initial DAG usually come from lowering function arguments and return values, and from lowering calls to other functions. This is where different calling conventions are applied, so the initial DAG may be different even for the same target if you change the calling convention.

Later on more differences appear from legalization (which each target needs to customize to match its needs), and custom DAG combines. All of this happens before the actual selection process starts.

-Krzysztof

To answer this---this seems to be a part of the return sequence, i.e. the part of the calling convention that dictates how a function passes return values to its caller. This is handled via LowerReturn in the target lowering object. Check SparcTargetLowering::LowerReturn for the Sparc implementation.

-Krzysztof

Thanks Tim and Krzysztof for pointing me in the right direction.

Indeed, when I started my backend I just blindly copied LowerFormalArguments and LowerReturn from SPARC backend, and that’s where these FrameIndex’es and Constant’s are coming from.

However, I haven’t managed to get a “Constant<>” in the DAG when compiling for X86. I’m interested in how it is lowered. Can you please give me some guidance on this?

How are you looking? When I run "llc -mtriple=x86_64-linux-gnu
-debug-only=isel" on your IR I get multiple instances of Constants. At
the very start is:

Initial selection DAG: %bb.0 'main:entry'
SelectionDAG has 18 nodes:
t0: ch = EntryToken
t7: i64 = Constant<0>
       t9: ch = store<(store 4 into %ir.retval)> t0, Constant:i32<0>,
FrameIndex:i64<0>, undef:i64
       t2: i32,ch = CopyFromReg t0, Register:i32 %0
     t11: ch = store<(store 4 into %ir.argc.addr)> t9, t2,
FrameIndex:i64<1>, undef:i64
     t4: i64,ch = CopyFromReg t0, Register:i64 %1
   t13: ch = store<(store 8 into %ir.argv.addr)> t11, t4,
FrameIndex:i64<2>, undef:i64
t16: ch,glue = CopyToReg t13, Register:i32 $eax, Constant:i32<0>
t17: ch = X86ISD::RET_FLAG t16, TargetConstant:i32<0>, Register:i32 $eax, t16:1

where the t16 line corresponds to what you've seen on SPARC.

Cheers.

Tim.

Strange. Even using exactly the same command line gives me following output:

bin/llc -debug-only=isel -mtriple=x86_64-linux-gnu 1.ll

Changing optimization level for Function main
Before: -O2 ; After: -O0
FastISel is enabled

=== main
Enabling fast-isel
Total amount of phi nodes to update: 0
*** MachineFunction at end of ISel ***

Machine code for function main: IsSSA, TracksLiveness

Frame Objects:
fi#0: size=4, align=4, at location [SP+8]
fi#1: size=4, align=4, at location [SP+8]
fi#2: size=8, align=8, at location [SP+8]
Function Live Ins: %edi in %0, %rsi in %2

%bb.0: derived from LLVM BB %entry
Live Ins: %edi %rsi
%2:gr64 = COPY %rsi; GR64:%2
%0:gr32 = COPY %edi; GR32:%0
%1:gr32 = COPY killed %0; GR32:%1,%0
%3:gr64 = COPY killed %2; GR64:%3,%2
%4:gr32 = MOV32r0 implicit-def %eflags; GR32:%4
MOV32mi %stack.0.retval, 1, %noreg, 0, %noreg, 0; mem:ST4[%retval]
MOV32mr %stack.1.argc.addr, 1, %noreg, 0, %noreg, %1; mem:ST4[%argc.addr] GR32:%1
MOV64mr %stack.2.argv.addr, 1, %noreg, 0, %noreg, %3; mem:ST8[%argv.addr] GR64:%3
%eax = COPY %4; GR32:%4
RETQ implicit %eax

End machine code for function main.

Restoring optimization level for Function main
Before: -O0 ; After: -O2

Ah, I think I can guess what's happening. I assume your 1.ll is
Clang's output, and you used the default optimization level (which is
-O0).

That means your function is actually tagged as "optnone" and LLVM
tries to use a different instruction selector called "FastISel" rather
than create a DAG at all. This speeds up compilation and improve the
debug experience, but not all targets support it. SPARC falls back to
the DAG because FastISel can't handle the function, but x86 is getting
through it ever creating a DAG.

To see the X86 DAG you can either remove the "optnone" attribute from
the .ll file or override the selector on the llc command-line:
-fast-isel=0.

Cheers.

Tim.

You’re absolutely right. Thanks a ton for your insights!