LLVM Backend for Z80. ADD -> replaced -> OR

Hello.

I am playing with LLVM and trying to make Z80 (Zilog Z80) backend.
The source code is attached.

I have succesfully made some simple test. But now I have problem with ADD instruction.
The source C code is:

typedef struct
{
unsigned char id1;
unsigned char id2;
unsigned char id3;

} testS;

void simple()
{
testS test;
test.id1 = 0x40;
test.id2 = 0x80;
test.id3 = 0xc0;
}

It produces this LLVM IR:

%struct.testS = type { i8, i8, i8 }

define void @simple() nounwind {
entry:
  %test = alloca %struct.testS, align 1
  %id1 = getelementptr inbounds %struct.testS* %test, i32 0, i32 0
  store i8 64, i8* %id1, align 1
  %id2 = getelementptr inbounds %struct.testS* %test, i32 0, i32 1
  store i8 -128, i8* %id2, align 1
  %id3 = getelementptr inbounds %struct.testS* %test, i32 0, i32 2
  store i8 -64, i8* %id3, align 1
  ret void
}

And with llc -filetype=asm -march=z80 a get this .s file:

.file "simple4.bc"
.text
.globl simple
.type simple,@function
simple: # @simple
# BB#0: # %entry
ld HL, 8
add HL,SP <--- problem line
ld DE, 1
ld BC, 2
ld (SP+6), BC
ld B, H
ld C, L
or BC, DE
ld (SP+4), BC
ld (HL),64
ld D, H
ld E, L
ld BC, (SP+6)
or DE, BC
ld BC, (SP+4)
ld H, B
ld L, C
ld (HL),-128
ld H, D
ld L, E
ld (HL),-64
$tmp0:
.size simple, ($tmp0)-simple

The problem is that llc replaces ADD instruction with OR.

Without defining OR16 function, the .S is not generated and claims that it cannot select OR instruction.
So I added OR16 (that's the or BC,DE piece of code).

But problem is that I am using the first two lines:

ld HL, 8
add HL,SP

For FrameIndex and the HL register will contain SP which is only two 2bytes aligned.
So "OR with 1" will work, but for second assigned :OR with 2" will not work.

I suspect that llc is assuming that HL will contain 8 (that's the start) and or-ing 8 with 1 or with 2 is ok.
But my HL has also added SP to it.
This is how my ISD::FrameIndex instruction look like:

def addHLdisp : Z80Instr<(outs HL16:$dst), (ins i16imm:$disp, GPR16:$src),
"ld $dst, $disp\n\tadd $dst,$src",
[(set HL16:$dst, (add GPR16:$src, (i16 imm:$disp)))]>;

So it say that HL16:$dst wich is only HL register, will be changed.
I also tried to change it to
<(outs HL16:$dst), (ins i16imm:$disp, SP16:$src),

But the output is the same.

Can someone tell me what I am doing wrong?

Peter.

llvm_to_send.tgz (20.4 KB)

Hi Peter,

I think the problem is that you did not explicitly define stack alignment
in Z80TargetMachine.cpp

DataLayout("e-p:16:8:8-i8:8:8-i16:8:8-i32:8:8-n8")

Try to add S16 to the string if your stack is 2-byte aligned. Refer to
http://llvm.org/docs/LangRef.html#datalayout .

If it does not work, try to specify the layout in the input module using
target layout directive.

David

Hello.

I have played with DataLayout and found a solution with is uknown to me.

I added S16 and also s0:16:16, but it had not worked.
Then I found that in Z80FrameLowering.h I am calling TargetFrameLowering
with stack aligment set to 8. So I changed it to 2 bytes. But this also
didn't help.

Then I changed llc to show TargetDataLayout and found that a option is set
to a0:0:64.
So I changed my Z80DataLayout to a0:0:16. And voila, it started to work and
ADD was not longer replaced by OR. Only for add+1 to or, which is correct.

So now I don't understand that a option in datalyout.
According doc it is alignment for aggregate type.

But anyhow thanks for help and pointing me in right direction.

Peter.

Hi Peter,

Hello.
I have played with DataLayout and found a solution with is uknown to me.
I added S16 and also s0:16:16, but it had not worked.
Then I found that in Z80FrameLowering.h I am calling TargetFrameLowering
with stack aligment set to 8. So I changed it to 2 bytes. But this also
didn't help.
Then I changed llc to show TargetDataLayout and found that a option is
set to a0:0:64.
So I changed my Z80DataLayout to a0:0:16. And voila, it started to work
and ADD was not longer replaced by OR. Only for add+1 to or, which is
correct.

The 'a' option in DataLayout specifies the alignment in bits of aggregate objects (structs and arrays) in memory. Since you had
specified before that structs were 8-byte-aligned, the compiler
"knew" that the low 3 bits of aggregate addresses would be 0, so
it did strength reduction on the addition to transform it to an OR.

If structs/arrays in memory are actually only 2-byte-aligned on Z80,
a0:0:16 is the right thing to do; it will only permit this kind of
strength reduction for operations on a single low bit. If
structs/arrays actually don't have any known alignment (a struct could
start at address 0x3, for example), you should change it to a0:0:8
(single-byte alignment).

-Matt