Lowering a reasonably complex struct seems to create over complex and invalid assembly fixups on some targets

When I compile this LLVM IR….

@0 = private constant [19 x i8] c"V4main10Brightness\00", section “__TEXT,__swift3_typeref, regular, no_dead_strip”
@1 = private constant [9 x i8] c"Vs5UInt8\00", section “__TEXT,__swift3_typeref, regular, no_dead_strip”
@2 = private constant [18 x i8] c"currentBrightness\00", section “__TEXT,__swift3_reflstr, regular, no_dead_strip”
@_TMRfV4main10Brightness =
internal constant <{ i32, i16, i16, i32, i32, i32, i32 }>
<{
i32 trunc
(i64 sub
(
i64 ptrtoint ([19 x i8]* @0 to i64),
i64 ptrtoint (<{ i32, i16, i16, i32, i32, i32, i32 }>* @_TMRfV4main10Brightness to i64)
)
to i32),

i16 0,
i16 12,
i32 1,
i32 0,

i32 trunc
(i64 sub
(i64 ptrtoint ([9 x i8]* @1 to i64),
i64 add (i64 ptrtoint (<{ i32, i16, i16, i32, i32, i32, i32 }>* @_TMRfV4main10Brightness to i64),
i64 16)
) to i32),
i32 trunc
(i64 sub
(
i64 ptrtoint ([18 x i8]* @2 to i64),
i64 add
(i64 ptrtoint (<{ i32, i16, i16, i32, i32, i32, i32 }>* @_TMRfV4main10Brightness to i64),
i64 20)
) to i32)
}>, section “__TEXT,__swift3_fieldmd, regular, no_dead_strip”, align 4

…on a couple of targets, it seems to produce invalid MC graphs that then fail to compile.
My chosen platform is AVR but it looks like it probably produces strange fixups on MIPS too.
I was initially chatting to the AVR rust team about this but I’m not sure it’s an AVR only problem. I’d like some help with understanding why these fixups are being created.
When compiled on AVR, I get the error "LLVM ERROR: expected relocatable expression”.
If I compile to asm, I get these fixups…
llc -march=avr -mcpu=atmega328p cause-relocation-error.ll -filetype=obj -debug 2>&1|
grep unnamed_4
Fixups:[
<MCFixup Offset:0 Value:(__unnamed_4&65535)-((_TMRfV4main10Brightness&65535)+16) Kind:2>]>,
(__unnamed_4, Index:0, ),
Fixups:[<MCFixup Offset:0 Value:(__unnamed_4&65535)-((_TMRfV4main10Brightness&65535)+16) Kind:2>]>,
(__unnamed_4, Index:0, ),
Fixups:[<MCFixup Offset:0 Value:(__unnamed_4&65535)-((_TMRfV4main10Brightness&65535)+16) Kind:2>]>,
(__unnamed_4, Index:0, ),
The output .s file in assembly:
.text
.file “min-3.ll”

.type __unnamed_3,@object ; @2
.section “__TEXT,__swift3_typeref, regular, no_dead_strip”,“a”,@progbits
.p2align 4
__unnamed_3:
.asciz “V4main10Brightness”
.size __unnamed_3, 19

.type __unnamed_4,@object ; @3
__unnamed_4:
.asciz “Vs5UInt8”
.size __unnamed_4, 9

.type __unnamed_5,@object ; @4
.section “__TEXT,__swift3_reflstr, regular, no_dead_strip”,“a”,@progbits
.p2align 4
__unnamed_5:
.asciz “currentBrightness”
.size __unnamed_5, 18

.type _TMRfV4main10Brightness,@object ; @_TMRfV4main10Brightness
.section “__TEXT,__swift3_fieldmd, regular, no_dead_strip”,“a”,@progbits
.p2align 2
_TMRfV4main10Brightness:
.long __unnamed_3-_TMRfV4main10Brightness
.short 0 ; 0x0
.short 12 ; 0xc
.long 1 ; 0x1
.long 0 ; 0x0
.long (__unnamed_4&65535)-((_TMRfV4main10Brightness&65535)+16)
.long (__unnamed_5&65535)-((_TMRfV4main10Brightness&65535)+20)
.size _TMRfV4main10Brightness, 24
If I try to compile this file with llvm-mc it doesn’t work, with much the same error.
Debugging with lldb, I found that in MCAssembler::evaluateFixup it calls MCExpr::evaluateAsRelocatableImpl and that returns false because it doesn’t accept a fixup like __unnamed_4&65535, binary MC expressions only seem to allow combining symbols using + or - (using EvaluateSymbolicAdd), not and, or, div, mul… etc.
On x86 the assembly output is simpler:
llc cause-relocation-error.ll -filetype=obj -debug 2>&1|
grep unnamed_4
Fixups:[
<MCFixup Offset:0 Value:.L__unnamed_4-(_TMRfV4main10Brightness+16) Kind:2>]>,
(.L__unnamed_4, Index:0, ),
Fixups:[
<MCFixup Offset:0 Value:.L__unnamed_4-(_TMRfV4main10Brightness+16) Kind:2>]>,
(.L__unnamed_4, Index:0, ),
Fixups:[
<MCFixup Offset:0 Value:.L__unnamed_4-(_TMRfV4main10Brightness+16) Kind:2>]>,
(.L__unnamed_4, Index:0, ),

Which compiles fine because the fixup combines symbols using only + and -, not &.
On MIPS, again the & operator is being put into expressions that end up in fixups.
llc -march=mips cause-relocation-error.ll -filetype=obj -debug 2>&1|
grep unnamed_4
Fixups:[
<MCFixup Offset:0 Value:(($__unnamed_4)&4294967295)-((_TMRfV4main10Brightness&4294967295)+16) Kind:2>]>,
($__unnamed_4, Index:0, ),
Fixups:[<MCFixup Offset:0 Value:(($__unnamed_4)&4294967295)-((_TMRfV4main10Brightness&4294967295)+16) Kind:2>]>,
($__unnamed_4, Index:0, ),
Fixups:[<MCFixup Offset:0 Value:(($__unnamed_4)&4294967295)-((_TMRfV4main10Brightness&4294967295)+16) Kind:2>]>,
($__unnamed_4, Index:0, ),
Which I don’t think would compile.
AVR is an experimental target but I think MIPS is mature? So I’m trying to get to the bottom of how LLVM is lowering to these expressions in fixups, when they cannot be evaluated by the MC layer?
Can anyone give any advice?

Hi Carl,

.long (__unnamed_4&65535)-((_TMRfV4main10Brightness&65535)+16)
.long (__unnamed_5&65535)-((_TMRfV4main10Brightness&65535)+20)

Ouch.

AVR is an experimental target but I think MIPS is mature? So I’m trying to get to the bottom of how LLVM is lowering to these expressions in fixups, when they cannot be evaluated by the MC layer?

I think the code you're after is in
lib/CodeGen/AsmPrinter/AsmPrinter.cpp, around line 2124 where it's
converting a ptrtoint ConstantExpr to an MCExpr.

Obviously the immediate fix is to do ptrtoint to i32 here instead of
truncating later. And to a certain extent this kind of problem is
inevitable. You can always and trivially write something in a Global
definition that is simply not implementable by the assembler
(relocations are not infinitely flexible on most platforms), so it's
the front-end's responsibility to emit constants that are valid.

What LLVM actually seems to be doing is assuming that expressions may
be evaluated at a precision beyond pointer width, which seems pretty
valid from a few test-cases I've run. When casting a bare pointer it's
fine to omit the "and", but for more complex expressions it can change
the value. Something like

    @tmp = global i8 zeroinitializer
    @var = global i64 ptrtoint(i8* getelementptr(i8, i8* @tmp, i32 1000) to i64)

cannot be simply emitted as ".quad tmp + 1000" on (say) 32-bit Mips
because that introduces a R_MIPS_64 relocation which will do 64-bit
arithmetic, but the GEP should wrap around at 2^32.

Of course, it's completely different on other targets: i386 does
32-bit arithmetic anyway (via R_386_32), ARM produces an error
message. So you could make a reasonable argument that the MC layer
and/or the translation in AsmPrinter.cpp is being a bit too
opinionated on how assemblers work and should be more generic.

Personally, I'm undecided about the platonic best option here. But I'd
probably quietly fix the front-end if it was my problem.

Cheers.

Tim.

Makes sense.

I’m trying to decide whether to patch anything in LLVM.

I think this feels like it falls into the category of:

“What you’re trying to do is an error, but the error message you’re getting is uninformative, unhelpful or misleading.”

The version of LLVM I’ve compiled doesn’t have ARM target support (for no particular reason, I just configured it for AVR and x86 only, by chance) so I can’t test the IR. I feel like the behaviour of AsmPrinter isn’t quite right on some platforms, it sounds like ARM has the right approach, just throw an error if you’re trying to lower LLVM IR that has 64 bit pointers onto platforms that could never support that, then throw an informative error, rather than creating MC Expressions that cannot be assembled by the lower layers.

I may take a bit of time to look at it one day and see if I can see an obvious fix. But as you’ve probably guessed, I’m very new to LLVM (I’m a swift developer by day and IoT/compiler hacker by night), so I’d probably get out of my depth too quickly.

The best/right fix for me is (as you suggested) compiling the swift front end from scratch to try and support a 16 bit pointer target triple (difficult according to Apple engineers I chatted to but possible and they might help a bit).

Thanks for your prompt help, really useful. And thanks for pinpointing where in the code the MC is being generated.

Have a great weekend!

Carl