1

I have compiled a cpp code and downloaded it to Arduino Uno for blinking an LED. The code works fine. However, when I convert it to .ll and from .ll to an object file then hex and upload, the code stops working. No LED blinks by the Arduino.

If I address the ports directly:

typedef unsigned char uint8_t;
typedef uint8_t * volatile port_type;

const port_type portB = (port_type) 0x25;
const port_type ddrB = (port_type) 0x24;

it will work fine but if I initialize port addressed via global constructor, it does not work:

int getPortB() {return 0x25;}
int getDdrB() {return 0x24;}

const port_type portB = (port_type) getPortB();
const port_type ddrB = (port_type) getDdrB();

This is because that global constructor is not called at all. If I call it from main function via

call addrspace(1) void @global_var_init()

it will work.

I use the following commands to compile and download the ll file to the Arduino uno:

llvm-as-9 blink1.ll -o blink1.bc 
llc-9 -filetype=obj blink1.bc 
avr-g++ -mmcu=atmega328p blink1.o -o blink1 
avr-objcopy -O ihex -R .eeprom blink1 blink1.hex 
avrdude -F -V -c arduino -p ATMEGA328P -P /dev/ttyUSB0 -b 115200 -U flash:w:blink1.hex

blink1.ll

; ModuleID = 'blink1.cpp'
source_filename = "blink1.cpp"
target datalayout = "e-P1-p:16:8-i8:8-i16:8-i32:8-i64:8-f32:8-f64:8-n8-a:8"
target triple = "avr"

@portB = dso_local global i8* null, align 1
@ddrB = dso_local global i8* null, align 1
@llvm.global_ctors = appending global [1 x { i32, void () addrspace(1)*, i8* }] [{ i32, void () addrspace(1)*, i8* } { i32 65535, void () addrspace(1)* @global_var_init, i8* null }]


; Function Attrs: noinline
define internal void @global_var_init() addrspace(1) {
  %1 = inttoptr i16 37 to i8*
  store volatile i8* %1, i8** @portB, align 1
  %2 = inttoptr i16 36 to i8*
  store volatile i8* %2, i8** @ddrB, align 1
  ret void
}

; Function Attrs: noinline nounwind optnone
define dso_local void @delay_500ms() addrspace(1) {
  call addrspace(0) void asm sideeffect "ldi  r19, 150 \0A\09ldi  r20, 128 \0A\09ldi  r23, 41 \0A\09L1: \0A\09dec  r20 \0A\09brne L1 \0A\09dec  r19 \0A\09brne L1 \0A\09dec  r23 \0A\09brne L1 \0A\09", ""() #3, !srcloc !2
  ret void
}

; Function Attrs: noinline norecurse nounwind optnone
define dso_local i16 @main() addrspace(1) {
  ; call addrspace(1) void @global_var_init()

  %1 = alloca i16, align 1
  store i16 0, i16* %1, align 1
  %2 = load volatile i8*, i8** @ddrB, align 1
  store i8 32, i8* %2, align 1
  br label %3

3:                                                ; preds = %0, %3
  %4 = load volatile i8*, i8** @portB, align 1
  store i8 32, i8* %4, align 1
  call addrspace(1) void @delay_500ms()
  %5 = load volatile i8*, i8** @portB, align 1
  store i8 0, i8* %5, align 1
  call addrspace(1) void @delay_500ms()
  br label %3
}

!0 = !{i32 1, !"wchar_size", i32 2}
!1 = !{!"clang version 9.0.1-+20210314105943+c1a0a213378a-1~exp1~20210314220516.107 "}
!2 = !{i32 1296, i32 1313, i32 1338, i32 1362, i32 1377, i32 1397, i32 1416, i32 1436, i32 1455, i32 1475, i32 1494}

Is this an LLVM bug or am I doing a mistake?

ar2015
  • 5,558
  • 8
  • 53
  • 110
  • Something has to call that constructor. Your path via .ll does not include anything that calls static constructors. Presumably the C++ compiler does link something suitable in. At a guess, memory allocation using new will also break for the same reason. – arnt Apr 12 '21 at 04:50
  • @arnt, how come clang works but not the ll file generated (and edited) by clang? – ar2015 Apr 12 '21 at 07:20
  • In one case, you ask clang to make a .ll file. In the other, you ask clang to generate executable code. That second case encompasses generating the .ll, generating native code from it, and linking (and perhaps some more minot details). I assume that clang links in some code that executes statically constructed objects if that seems necessary, and you don't link in that. As likely as not, that code comes from the same library that provides operator new, exception handling etc. – arnt Apr 12 '21 at 07:29

0 Answers0