Updated llc does not compile my .ll files any more [addrspace on AVR problem?]

Hi,

I’ve come back and updated my llvm toolset with modern code (my branch was about 1-2 years old) and now the llvm IR files produced by my front end no longer compile with llc.

Here is a sample of llvm ir produced by my front end (it’s a standard version 3.1 build of swift from the swift.org open source website).

; ModuleID = 'main.ll'
source_filename = "main.ll"
target datalayout = "e-m:o-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx10.9"

%Vs5UInt8 = type <{ i8 }>
%Sp = type <{ i8* }>
%Vs5Int32 = type <{ i32 }>
%Sb = type <{ i1 }>
%Vs6UInt16 = type <{ i16 }>

@Started0 = external local_unnamed_addr global i8*, align 8
@D13 = external local_unnamed_addr constant i8, align 1
@OUTPUT = external local_unnamed_addr constant i8, align 1
@HIGH = external local_unnamed_addr constant i8, align 1
@LOW = external local_unnamed_addr constant i8, align 1
@_Tv4main12bufferLengthVs5UInt8 = hidden local_unnamed_addr global %Vs5UInt8 zeroinitializer, align 1
@_Tv4main8irBufferGSpVs6UInt16_ = hidden local_unnamed_addr global %Sp zeroinitializer, align 8
@_Tv4main9redBufferGSpVs6UInt16_ = hidden local_unnamed_addr global %Sp zeroinitializer, align 8
@_Tv4main4SPO2Vs5Int32 = hidden global %Vs5Int32 zeroinitializer, align 4
@_Tv4main11isSPO2ValidSb = hidden global %Sb zeroinitializer, align 1
@_Tv4main2HRVs5Int32 = hidden global %Vs5Int32 zeroinitializer, align 4
@_Tv4main9isHRValidSb = hidden global %Sb zeroinitializer, align 1
@SpO20 = external local_unnamed_addr global i8*, align 8
@SpO21 = external local_unnamed_addr global i8*, align 8
@HR0 = external local_unnamed_addr global i8*, align 8
@HR1 = external local_unnamed_addr global i8*, align 8
@_Tv4main12readLEDStateSb = hidden local_unnamed_addr global %Sb zeroinitializer, align 1
@_swift_slowAlloc = external local_unnamed_addr global i8* (i64, i64)*
@_swift_slowDealloc = external local_unnamed_addr global void (i8*, i64, i64)*
@__swift_reflection_version = linkonce_odr hidden constant i16 1
@llvm.used = appending global [1 x i8*] [i8* bitcast (i16* @__swift_reflection_version to i8*)], section "llvm.metadata"

... snip ...

; Function Attrs: nounwind
define hidden void @_TFe4mainRxzVs6UInt16rSp7releasefT_T_(i8*) local_unnamed_addr #3 {
entry:
  tail call void @swift_rt_swift_slowDealloc(i8* %0, i64 0, i64 1) #6
  ret void
}

... snip ...

; Function Attrs: noinline nounwind
define linkonce_odr hidden void @swift_rt_swift_slowDealloc(i8*, i64, i64) local_unnamed_addr #2 {
entry:
  %load = load void (i8*, i64, i64)*, void (i8*, i64, i64)** @_swift_slowDealloc, align 8
  tail call void %load(i8* %0, i64 %1, i64 %2) #6
  ret void
}

... snip ...

; Function Attrs: argmemonly nounwind
declare void @llvm.memset.p0i8.i64(i8* nocapture writeonly, i8, i64, i32, i1) #5

attributes #0 = { noreturn "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+fxsr,+mmx,+x87,+sse,+sse2,+sse3" }
attributes #1 = { "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+fxsr,+mmx,+x87,+sse,+sse2,+sse3" }
attributes #2 = { noinline nounwind }
attributes #3 = { nounwind "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+fxsr,+mmx,+x87,+sse,+sse2,+sse3" }
attributes #4 = { norecurse nounwind "no-frame-pointer-elim"="true" "no-frame-pointer-elim-non-leaf" "target-cpu"="core2" "target-features"="+ssse3,+cx16,+fxsr,+mmx,+x87,+sse,+sse2,+sse3" }
attributes #5 = { argmemonly nounwind }
attributes #6 = { nounwind }

I am using this command to build it.

llc -O3 -march=avr -mcpu=atmega328p -filetype=obj -function-sections -o main.o main.clean.ll

On my old llvm source code it used to work but now it fails with errors like...

llc: error: /Code/llvm-project/build/bin/llc: main.clean.ll:101:22: error: '%load' defined with type 'i8* (i64, i64)*' but expected 'i8* (i64, i64) addrspace(1)*'
  %2 = tail call i8* %load(i64 %0, i64 %1) #5

I am guessing this is because the AVR backend is now aware of address spaces or something like that and expects all functions to be stored in addrspace 1? Then also presumably function pointers are also typed including an addrspace(1), but the old llvm ir being output by my swift 3.1 front end does not know about this.

I would like a way forward for this, so I can update my product to use modern llvm code. It seems there are two approaches:

1) easiest would be if I can set some command line flag on llc to ignore address spaces so it will all compile

2) alternatively there might be some hack or regex/sed thing i can do to transform the llvm ir text file to be compatible to the new llc backend (unlikely)

3) last and hardest, i can try to get hold of the source code for swift 3.1 and patch it to be aware of address spaces somehow? or more likely get more up to date swift source (5.1) and work on that.

I've got a strong suspicion people are going to tell me 3 is the only viable option if I want an updated llvm back end, in which case, can anyone give me pointers how to add addrspace intelligence into swift. Swift is presumably linked against llvm libraries in order to emit the text llvm ir files so is it just a question of setting swift to output to an avr target and it's all automatic? I'm happy hacking around the llvm code but have no idea what bits of code to look at.

Thanks,
Carl

Like the error message says, function pointers need to have a type of the form "'i8* (i64, i64) addrspace(1)*". You might be able to use some sed hack to get around that particular issue (search-replace "'i8* (i64, i64)*" with "'i8* (i64, i64) addrspace(1)*" etc.).

Using the LLVM C++ API, setting the address-space correctly is just a matter of passing it to "llvm::PointerType::get()" whenever the code constructs a function pointer type.

More generally, it's not wise to take IR generated for one target, and try to compile it for another target; you're likely to run into obscure issues with the datalayout/ABI. You probably need to tell the frontend you're actually compiling for AVR. (For clang, you'd use the "-target" flag; not sure what the equivalent is for Swift.)

-Eli

That’s useful info, thanks.

I think it will be useful for me to understand the connection, why this type of pointer is being emitted now.

Do you have any suggestions where i can look to find the platform specific code that is making function pointers go into addrspace?

Carl

p.s. I am also working on passing the avr target flag to swift, but swift itself had (has?) limitations that make it crash with some aspects of the avr platform (16 bit pointers). Also until now I’ve been using off the shelf binaries for swift to make sure it’s behaviour is predictable as it affects the language my customers experience (I patched my custom llc/llvm instead).

See the description of "P<address space>" at http://llvm.org/docs/LangRef.html#langref-datalayout .

-Eli

Cool. That explains a lot!

Sorry if this is a total n00b question, but… how does the datalayout string get overridden?

in llvm/lib/Target/AVR/AVRTargetMachine.cpp I can see the code that determines the default datalayout for AVR…

static const char *AVRDataLayout = "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8”;

However in the LLVM iR below, the target datalayout was present and didn’t contain “P1”. So (I’m sure i’m not the first person to ask this but) I’m a bit confused about what the datalayout string in the llvm ir file is doing?

When llc reads this file, it seems like it is overriding at least the P setting and probably other settings, based on the target triple passed on the command line?

I even explicitly modified the target datalayout string like

target datalayout = "e-P0-m:o-i64:64-f80:128-n8:16:32:64-S128"

And I’m still getting the same error…

llc: error: /Code/llvm-project/build/bin/llc: main.clean.ll:101:22: error: '%load' defined with type 'i8* (i64, i64)*' but expected 'i8* (i64, i64) addrspace(1)*'
  %2 = tail call i8* %load(i64 %0, i64 %1) #5

So llc is completely ignoring the target datalayout?

Sorry i’m asking dumb questions…

Carl

Yes, llc ignores the datalayout specified in the file, in favor of whatever the target tells it the datalayout should be. See compileModule In llvm/tools/llc/llc.cpp.

-Eli

OK, that is everything I needed. Thank you so much for your help Eli!

Carl