Ensuring chain dependencies with expansion to libcalls

Hi all,

Our target does not have native support for 64-bit integers, so we rely on library calls for certain operations (like sdiv). We recently ran into a problem where these operations that are expanded to library calls aren’t maintaining the proper ordering in relation to other chains in the DAG.

The following snippet of a DAG demonstrates the problem.

t0: ch = EntryToken
t2: i64,ch,glue = CopyFromReg t0, Register:i64 %reg0
t4: i64,ch,glue = CopyFromReg t2:1, Register:i64 %reg1, t2:1
t6: i64,ch,glue = CopyFromReg t4:1, Register:i64 %reg2, t4:1
t8: i64,ch,glue = CopyFromReg t6:1, Register:i64 %reg3, t6:1
t11: ch = CopyToReg t0, Register:i64 %vreg0, t2
t13: ch = CopyToReg t0, Register:i64 %vreg1, t4
t15: ch = CopyToReg t0, Register:i64 %vreg2, t8
t26: ch = TokenFactor t11, t13, t15, t2:1, t4:1, t6:1, t8:1
t16: i64 = sdiv t2, t4

Before legalization, there is a single sdiv node. After legalization, this has been expanded to a call sequence:

t0: ch = EntryToken
t2: i64,ch,glue = CopyFromReg t0, Register:i64 %reg0
t4: i64,ch,glue = CopyFromReg t2:1, Register:i64 %reg1, t2:1
t6: i64,ch,glue = CopyFromReg t4:1, Register:i64 %reg2, t4:1
t8: i64,ch,glue = CopyFromReg t6:1, Register:i64 %reg3, t6:1
t46: ch,glue = callseq_start t0, TargetConstant:i32<0>
t47: ch,glue = CopyToReg t46, Register:i64 %reg0, t2
t48: ch,glue = CopyToReg t47, Register:i64 %reg1, t4, t47:1
t50: ch,glue = SHAVEISD::CALL t48, TargetExternalSymbol:i32’__divdi3’, Register:i64 %reg0, Register:i64 %reg1, RegisterMask:Untyped, t48:1
t51: ch,glue = callseq_end t50, TargetConstant:i32<0>, TargetExternalSymbol:i32’__divdi3’, t50:1
t52: i64,ch,glue = CopyFromReg t51, Register:i64 %reg0, t51:1
t11: ch = CopyToReg t0, Register:i64 %vreg0, t2
t13: ch = CopyToReg t0, Register:i64 %vreg1, t4
t15: ch = CopyToReg t0, Register:i64 %vreg2, t8
t26: ch = TokenFactor t11, t13, t15, t2:1, t4:1, t6:1, t8:1

Since the sdiv node is not part of a chain, the EntryToken is used as the starting point of the chain for the library call. This means that the ordering between the sequence of CopyFromReg nodes and the call sequence can be changed. Specifically, we are seeing the two copies from %reg2 and %reg3 being moved to after the call sequence. This is a problem since the library call clobbers both of these registers (since they are caller-saved registers).

If I manually call __divdi3 instead of using / in the source code that generates this DAG, then we get the following snippet:

t0: ch = EntryToken
t2: i64,ch,glue = CopyFromReg t0, Register:i64 %reg0
t4: i64,ch,glue = CopyFromReg t2:1, Register:i64 %reg1, t2:1
t6: i64,ch,glue = CopyFromReg t4:1, Register:i64 %reg2, t4:1
t8: i64,ch,glue = CopyFromReg t6:1, Register:i64 %reg3, t6:1
t9: ch = TokenFactor t2:1, t4:1, t6:1, t8:1
t19: ch,glue = callseq_start t9, TargetConstant:i32<0>
t20: ch,glue = CopyToReg t19, Register:i64 %reg0, t2
t21: ch,glue = CopyToReg t20, Register:i64 %reg1, t4, t20:1
t23: ch,glue = SHAVEISD::CALL t21, TargetGlobalAddress:i32<i64 (i64, i64)* @__divdi3> 0, Register:i64 %reg0, Register:i64 %reg1, RegisterMask:Untyped, t21:1
t24: ch,glue = callseq_end t23, TargetConstant:i32<0>, TargetGlobalAddress:i32<i64 (i64, i64)* @__divdi3> 0, t23:1
t25: i64,ch,glue = CopyFromReg t24, Register:i64 %reg0, t24:1
t11: ch = CopyToReg t0, Register:i64 %vreg0, t2
t13: ch = CopyToReg t0, Register:i64 %vreg1, t4
t15: ch = CopyToReg t0, Register:i64 %vreg2, t8
t31: ch = TokenFactor t11, t13, t15, t25:1

LLVM correctly uses a TokenFactor to pull the CopyFromReg nodes together and uses it as the input chain to the call sequence.

Is there a way that I can tell LLVM that i64 sdiv operations have side-effects for our target and require an input chain?

Thanks,

Stephen

“CopyFromReg t0, Register:i64 %reg0” is your problem: as you’ve discovered, you can’t model function arguments like this. If you look at the debug output for an in-tree target (e.g. 32-bit ARM), you’ll find that the CopyFromReg nodes for register arguments are copies from virtual registers. See MachineFunction::addLiveIn. -Eli

Thanks Eli,

This was exactly the problem. Using virtual registers for our function arguments has fixed it.

All the best,

Stephen