-
Notifications
You must be signed in to change notification settings - Fork 14
Indirect call address is possibly being loaded incorrectly #65
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Operational note: I'd highly encourage you to read up on GitHub's Markdown syntax. Pasting giant blocks of code without proper formatting makes it extremely difficult to read and follow along. I've been editing your previous posts, but I'd rather not need to. |
OK, good point, sorry shepmaster... I'll try to be more careful. Thank you for tidying up after me! |
More analysis... Looking at this fairly simple reductive case, and using clang/llvm to study the back end behaviour instead of swiftc/llvm (where I originally discovered the bug), created a reduced test case (extract here from analog.c):
Looking at the assembly for _analogReadAsyncTest, I can see a difference in how the fixups are generated. analog.s is assembly created by avr-gcc -S analog.c.txt
The fixups generated by avr-gcc look like R_AVR_LO8_LDI_GS / R_AVR_HI8_LDI_GS or (equivalently?) lo8(gs(_analogReadAsyncTestCallback)) / hi8(gs(_analogReadAsyncTestCallback)). Whereas the fixups generated by clang/llvm look like lo8(_analogReadAsyncTestCallback) / hi8(_analogReadAsyncTestCallback). |
FYI, the relocs are in include/llvm/Support/ELFRelocs/AVR.def
|
If we modify the back end to use these _gs fixups, a few changes will be needed. For example in MCTargetDesc/AVRAsmBackend.cpp
and in MCTargetDesc/AVRFixupKinds.h
the comment should be filled out to help future programmers. Finally in MCTargetDesc/AVRMCExpr.cpp, getFixupKind should be extended:
to cover the _GS fixups. Any other fixes needed can be picked up by a search for fixup_lo8_ldi. This also points to the next step, the enum...
from MCTargetDesc/AVRMCExpr.h should be extended with _GS types. And search for associated improvements using VK_AVR_LO8 to make sure that machine code writing, assembly writing and assembly reading all work. The driving engine of this should probably(?) be an extra target flag in AVRMCInstLower::lowerSymbolOperand from AVRMCInstLower.cpp, where the machine code instance (MC) for the fixups are created:
The target flags are defined in AVRInstrInfo.h:
We could add an extra flag in there like:
|
I am not clear where the target flags are set and how we would pick up that the address being interpreted is that of a function so the _GS flag would need to be applied. Any help gratefully received!! |
The IR in the description is missing a forward declaration, replicating the entire file in one block here for easy copy-paste. define linkonce_odr hidden void @_TToFF3AVR15analogReadAsyncFT3pinVs5UInt88callbackcVs6UInt16T__T_U_FS1_T_(i16 zeroext) #2 {
entry:
call void @_TFF3AVR15analogReadAsyncFT3pinVs5UInt88callbackcVs6UInt16T__T_U_FS1_T_(i16 %0) #3
ret void
} EDIT Didn't realise there were file links in the description |
I've looked into this a lot more I believe that we should be using the Currently in Here is the predicate we should be able to use: int AddrSpace = ((llvm::PointerType*)MO.getGlobal()->getType())->getAddressSpace();
switch (AddrSpace) {
case AVR::DataMemory: /* use the standard LDI_* fixup to access ram */
case AVR::FlashMemory: /* use the *_GS variant to access program memory */
} The reason we cannot is because LLVM defaults everything to address space zero (data memory, RAM), including functions and global variables. We will need to do something similar to #47. In that change we made switch lookup table global variables default to address space one (program memory). We should do the same to functions, any anything else that it make sense on. Once we have the correct address space information associated with all our pointer types, we can then check the address space inside |
Here's the C for the full testcase #include <avr/io.h>
#define true 1
#define false 0
typedef void (*Callback)(unsigned short);
void _analogReadAsync(unsigned char pin, Callback);
void _digitalWrite(unsigned char pin, _Bool a);
void _analogReadAsyncTestCallback(unsigned short value) {
_digitalWrite(13, true);
}
void _analogReadAsyncTest(unsigned char pin) {
_analogReadAsync(pin,_analogReadAsyncTestCallback);
}
__attribute__((noinline)) unsigned char pinMask(unsigned char pin) {
if (pin<8) {
return ((unsigned char)1) << pin;
} else if (pin<14) {
return ((unsigned char)1) << (pin - (unsigned char)8);
}
return 0;
}
// reads as false if you pass an invalid pin number like 14
_Bool _digitalRead(unsigned char pin) {
unsigned char mask = pinMask(pin);
if (!mask) return false;
if (pin<8) {
return PIND & mask;
} else {
return PINB & mask;
}
return false;
}
/*
These functions are stupidly verbose and spread out to try and force the compiler to make code that works.
Currently there are bugs in the AVR back end that produce non functional code unless we force its hand.
*/
__attribute__((noinline)) void resetPortD(unsigned char pin) {
unsigned char mask = pinMask(pin);
if (mask) {
PORTD &= ~mask;
}
}
__attribute__((noinline)) void resetPortB(unsigned char pin) {
unsigned char mask = pinMask(pin);
if (mask) {
PORTB &= ~mask;
}
}
__attribute__((noinline)) void setPortD(unsigned char pin) {
unsigned char mask = pinMask(pin);
if (mask) {
PORTD |= mask;
}
}
__attribute__((noinline)) void setPortB(unsigned char pin) {
unsigned char mask = pinMask(pin);
if (mask) {
PORTB |= mask;
}
}
void _digitalWrite(unsigned char pin, _Bool value) {
if (value) {
if (pin<8) {
setPortD(pin);
} else {
setPortB(pin);
}
} else {
if (pin<8) {
resetPortD(pin);
} else {
resetPortB(pin);
}
}
}
void enablePortDWrite(unsigned char pin) {
unsigned char mask = pinMask(pin);
if (mask) {
DDRD |= mask;
}
}
void enablePortBWrite(unsigned char pin) {
unsigned char mask = pinMask(pin);
if (mask) {
DDRB |= mask;
}
}
void disablePortDWrite(unsigned char pin) {
unsigned char mask = pinMask(pin);
if (mask) {
DDRD &= ~mask;
}
}
void disablePortBWrite(unsigned char pin) {
unsigned char mask = pinMask(pin);
if (mask) {
DDRB &= ~mask;
}
}
void _pinMode(unsigned char pin, _Bool write) {
if (write) {
if (pin<8) {
enablePortDWrite(pin);
} else {
enablePortBWrite(pin);
}
} else {
if (pin<8) {
disablePortDWrite(pin);
} else {
disablePortBWrite(pin);
}
}
} Here it is in IR form (note: uses IR syntax from LLVM master) ; ModuleID = 'test.c'
source_filename = "test.c"
target datalayout = "e-p:16:16:16-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-n8"
target triple = "avr-atmel-none"
; Function Attrs: noinline nounwind optnone
define void @_analogReadAsyncTestCallback(i16 zeroext %value) #0 {
entry:
%value.addr = alloca i16, align 2
store i16 %value, i16* %value.addr, align 2
call void @_digitalWrite(i8 zeroext 13, i1 zeroext true)
ret void
}
; Function Attrs: noinline nounwind optnone
define void @_digitalWrite(i8 zeroext %pin, i1 zeroext %value) #0 {
entry:
%pin.addr = alloca i8, align 1
%value.addr = alloca i8, align 1
store i8 %pin, i8* %pin.addr, align 1
%frombool = zext i1 %value to i8
store i8 %frombool, i8* %value.addr, align 1
%0 = load i8, i8* %value.addr, align 1
%tobool = trunc i8 %0 to i1
br i1 %tobool, label %if.then, label %if.else3
if.then: ; preds = %entry
%1 = load i8, i8* %pin.addr, align 1
%conv = zext i8 %1 to i16
%cmp = icmp slt i16 %conv, 8
br i1 %cmp, label %if.then2, label %if.else
if.then2: ; preds = %if.then
%2 = load i8, i8* %pin.addr, align 1
call void @setPortD(i8 zeroext %2)
br label %if.end
if.else: ; preds = %if.then
%3 = load i8, i8* %pin.addr, align 1
call void @setPortB(i8 zeroext %3)
br label %if.end
if.end: ; preds = %if.else, %if.then2
br label %if.end10
if.else3: ; preds = %entry
%4 = load i8, i8* %pin.addr, align 1
%conv4 = zext i8 %4 to i16
%cmp5 = icmp slt i16 %conv4, 8
br i1 %cmp5, label %if.then7, label %if.else8
if.then7: ; preds = %if.else3
%5 = load i8, i8* %pin.addr, align 1
call void @resetPortD(i8 zeroext %5)
br label %if.end9
if.else8: ; preds = %if.else3
%6 = load i8, i8* %pin.addr, align 1
call void @resetPortB(i8 zeroext %6)
br label %if.end9
if.end9: ; preds = %if.else8, %if.then7
br label %if.end10
if.end10: ; preds = %if.end9, %if.end
ret void
}
; Function Attrs: noinline nounwind optnone
define void @_analogReadAsyncTest(i8 zeroext %pin) #0 {
entry:
%pin.addr = alloca i8, align 1
store i8 %pin, i8* %pin.addr, align 1
%0 = load i8, i8* %pin.addr, align 1
call void @_analogReadAsync(i8 zeroext %0, void (i16)* @_analogReadAsyncTestCallback)
ret void
}
declare void @_analogReadAsync(i8 zeroext, void (i16)*) #1
; Function Attrs: noinline nounwind optnone
define zeroext i8 @pinMask(i8 zeroext %pin) #0 {
entry:
%retval = alloca i8, align 1
%pin.addr = alloca i8, align 1
store i8 %pin, i8* %pin.addr, align 1
%0 = load i8, i8* %pin.addr, align 1
%conv = zext i8 %0 to i16
%cmp = icmp slt i16 %conv, 8
br i1 %cmp, label %if.then, label %if.else
if.then: ; preds = %entry
%1 = load i8, i8* %pin.addr, align 1
%conv2 = zext i8 %1 to i16
%shl = shl i16 1, %conv2
%conv3 = trunc i16 %shl to i8
store i8 %conv3, i8* %retval, align 1
br label %return
if.else: ; preds = %entry
%2 = load i8, i8* %pin.addr, align 1
%conv4 = zext i8 %2 to i16
%cmp5 = icmp slt i16 %conv4, 14
br i1 %cmp5, label %if.then7, label %if.end
if.then7: ; preds = %if.else
%3 = load i8, i8* %pin.addr, align 1
%conv8 = zext i8 %3 to i16
%sub = sub nsw i16 %conv8, 8
%shl9 = shl i16 1, %sub
%conv10 = trunc i16 %shl9 to i8
store i8 %conv10, i8* %retval, align 1
br label %return
if.end: ; preds = %if.else
br label %if.end11
if.end11: ; preds = %if.end
store i8 0, i8* %retval, align 1
br label %return
return: ; preds = %if.end11, %if.then7, %if.then
%4 = load i8, i8* %retval, align 1
ret i8 %4
}
; Function Attrs: noinline nounwind optnone
define zeroext i1 @_digitalRead(i8 zeroext %pin) #0 {
entry:
%retval = alloca i1, align 1
%pin.addr = alloca i8, align 1
%mask = alloca i8, align 1
store i8 %pin, i8* %pin.addr, align 1
%0 = load i8, i8* %pin.addr, align 1
%call = call zeroext i8 @pinMask(i8 zeroext %0)
store i8 %call, i8* %mask, align 1
%1 = load i8, i8* %mask, align 1
%tobool = icmp ne i8 %1, 0
br i1 %tobool, label %if.end, label %if.then
if.then: ; preds = %entry
store i1 false, i1* %retval, align 1
br label %return
if.end: ; preds = %entry
%2 = load i8, i8* %pin.addr, align 1
%conv = zext i8 %2 to i16
%cmp = icmp slt i16 %conv, 8
br i1 %cmp, label %if.then2, label %if.else
if.then2: ; preds = %if.end
%3 = load volatile i8, i8* inttoptr (i16 41 to i8*), align 1
%conv3 = zext i8 %3 to i16
%4 = load i8, i8* %mask, align 1
%conv4 = zext i8 %4 to i16
%and = and i16 %conv3, %conv4
%tobool5 = icmp ne i16 %and, 0
store i1 %tobool5, i1* %retval, align 1
br label %return
if.else: ; preds = %if.end
%5 = load volatile i8, i8* inttoptr (i16 35 to i8*), align 1
%conv6 = zext i8 %5 to i16
%6 = load i8, i8* %mask, align 1
%conv7 = zext i8 %6 to i16
%and8 = and i16 %conv6, %conv7
%tobool9 = icmp ne i16 %and8, 0
store i1 %tobool9, i1* %retval, align 1
br label %return
return: ; preds = %if.else, %if.then2, %if.then
%7 = load i1, i1* %retval, align 1
ret i1 %7
}
; Function Attrs: noinline nounwind optnone
define void @resetPortD(i8 zeroext %pin) #0 {
entry:
%pin.addr = alloca i8, align 1
%mask = alloca i8, align 1
store i8 %pin, i8* %pin.addr, align 1
%0 = load i8, i8* %pin.addr, align 1
%call = call zeroext i8 @pinMask(i8 zeroext %0)
store i8 %call, i8* %mask, align 1
%1 = load i8, i8* %mask, align 1
%tobool = icmp ne i8 %1, 0
br i1 %tobool, label %if.then, label %if.end
if.then: ; preds = %entry
%2 = load i8, i8* %mask, align 1
%conv = zext i8 %2 to i16
%neg = xor i16 %conv, -1
%3 = load volatile i8, i8* inttoptr (i16 43 to i8*), align 1
%conv1 = zext i8 %3 to i16
%and = and i16 %conv1, %neg
%conv2 = trunc i16 %and to i8
store volatile i8 %conv2, i8* inttoptr (i16 43 to i8*), align 1
br label %if.end
if.end: ; preds = %if.then, %entry
ret void
}
; Function Attrs: noinline nounwind optnone
define void @resetPortB(i8 zeroext %pin) #0 {
entry:
%pin.addr = alloca i8, align 1
%mask = alloca i8, align 1
store i8 %pin, i8* %pin.addr, align 1
%0 = load i8, i8* %pin.addr, align 1
%call = call zeroext i8 @pinMask(i8 zeroext %0)
store i8 %call, i8* %mask, align 1
%1 = load i8, i8* %mask, align 1
%tobool = icmp ne i8 %1, 0
br i1 %tobool, label %if.then, label %if.end
if.then: ; preds = %entry
%2 = load i8, i8* %mask, align 1
%conv = zext i8 %2 to i16
%neg = xor i16 %conv, -1
%3 = load volatile i8, i8* inttoptr (i16 37 to i8*), align 1
%conv1 = zext i8 %3 to i16
%and = and i16 %conv1, %neg
%conv2 = trunc i16 %and to i8
store volatile i8 %conv2, i8* inttoptr (i16 37 to i8*), align 1
br label %if.end
if.end: ; preds = %if.then, %entry
ret void
}
; Function Attrs: noinline nounwind optnone
define void @setPortD(i8 zeroext %pin) #0 {
entry:
%pin.addr = alloca i8, align 1
%mask = alloca i8, align 1
store i8 %pin, i8* %pin.addr, align 1
%0 = load i8, i8* %pin.addr, align 1
%call = call zeroext i8 @pinMask(i8 zeroext %0)
store i8 %call, i8* %mask, align 1
%1 = load i8, i8* %mask, align 1
%tobool = icmp ne i8 %1, 0
br i1 %tobool, label %if.then, label %if.end
if.then: ; preds = %entry
%2 = load i8, i8* %mask, align 1
%conv = zext i8 %2 to i16
%3 = load volatile i8, i8* inttoptr (i16 43 to i8*), align 1
%conv1 = zext i8 %3 to i16
%or = or i16 %conv1, %conv
%conv2 = trunc i16 %or to i8
store volatile i8 %conv2, i8* inttoptr (i16 43 to i8*), align 1
br label %if.end
if.end: ; preds = %if.then, %entry
ret void
}
; Function Attrs: noinline nounwind optnone
define void @setPortB(i8 zeroext %pin) #0 {
entry:
%pin.addr = alloca i8, align 1
%mask = alloca i8, align 1
store i8 %pin, i8* %pin.addr, align 1
%0 = load i8, i8* %pin.addr, align 1
%call = call zeroext i8 @pinMask(i8 zeroext %0)
store i8 %call, i8* %mask, align 1
%1 = load i8, i8* %mask, align 1
%tobool = icmp ne i8 %1, 0
br i1 %tobool, label %if.then, label %if.end
if.then: ; preds = %entry
%2 = load i8, i8* %mask, align 1
%conv = zext i8 %2 to i16
%3 = load volatile i8, i8* inttoptr (i16 37 to i8*), align 1
%conv1 = zext i8 %3 to i16
%or = or i16 %conv1, %conv
%conv2 = trunc i16 %or to i8
store volatile i8 %conv2, i8* inttoptr (i16 37 to i8*), align 1
br label %if.end
if.end: ; preds = %if.then, %entry
ret void
}
; Function Attrs: noinline nounwind optnone
define void @enablePortDWrite(i8 zeroext %pin) #0 {
entry:
%pin.addr = alloca i8, align 1
%mask = alloca i8, align 1
store i8 %pin, i8* %pin.addr, align 1
%0 = load i8, i8* %pin.addr, align 1
%call = call zeroext i8 @pinMask(i8 zeroext %0)
store i8 %call, i8* %mask, align 1
%1 = load i8, i8* %mask, align 1
%tobool = icmp ne i8 %1, 0
br i1 %tobool, label %if.then, label %if.end
if.then: ; preds = %entry
%2 = load i8, i8* %mask, align 1
%conv = zext i8 %2 to i16
%3 = load volatile i8, i8* inttoptr (i16 42 to i8*), align 1
%conv1 = zext i8 %3 to i16
%or = or i16 %conv1, %conv
%conv2 = trunc i16 %or to i8
store volatile i8 %conv2, i8* inttoptr (i16 42 to i8*), align 1
br label %if.end
if.end: ; preds = %if.then, %entry
ret void
}
; Function Attrs: noinline nounwind optnone
define void @enablePortBWrite(i8 zeroext %pin) #0 {
entry:
%pin.addr = alloca i8, align 1
%mask = alloca i8, align 1
store i8 %pin, i8* %pin.addr, align 1
%0 = load i8, i8* %pin.addr, align 1
%call = call zeroext i8 @pinMask(i8 zeroext %0)
store i8 %call, i8* %mask, align 1
%1 = load i8, i8* %mask, align 1
%tobool = icmp ne i8 %1, 0
br i1 %tobool, label %if.then, label %if.end
if.then: ; preds = %entry
%2 = load i8, i8* %mask, align 1
%conv = zext i8 %2 to i16
%3 = load volatile i8, i8* inttoptr (i16 36 to i8*), align 1
%conv1 = zext i8 %3 to i16
%or = or i16 %conv1, %conv
%conv2 = trunc i16 %or to i8
store volatile i8 %conv2, i8* inttoptr (i16 36 to i8*), align 1
br label %if.end
if.end: ; preds = %if.then, %entry
ret void
}
; Function Attrs: noinline nounwind optnone
define void @disablePortDWrite(i8 zeroext %pin) #0 {
entry:
%pin.addr = alloca i8, align 1
%mask = alloca i8, align 1
store i8 %pin, i8* %pin.addr, align 1
%0 = load i8, i8* %pin.addr, align 1
%call = call zeroext i8 @pinMask(i8 zeroext %0)
store i8 %call, i8* %mask, align 1
%1 = load i8, i8* %mask, align 1
%tobool = icmp ne i8 %1, 0
br i1 %tobool, label %if.then, label %if.end
if.then: ; preds = %entry
%2 = load i8, i8* %mask, align 1
%conv = zext i8 %2 to i16
%neg = xor i16 %conv, -1
%3 = load volatile i8, i8* inttoptr (i16 42 to i8*), align 1
%conv1 = zext i8 %3 to i16
%and = and i16 %conv1, %neg
%conv2 = trunc i16 %and to i8
store volatile i8 %conv2, i8* inttoptr (i16 42 to i8*), align 1
br label %if.end
if.end: ; preds = %if.then, %entry
ret void
}
; Function Attrs: noinline nounwind optnone
define void @disablePortBWrite(i8 zeroext %pin) #0 {
entry:
%pin.addr = alloca i8, align 1
%mask = alloca i8, align 1
store i8 %pin, i8* %pin.addr, align 1
%0 = load i8, i8* %pin.addr, align 1
%call = call zeroext i8 @pinMask(i8 zeroext %0)
store i8 %call, i8* %mask, align 1
%1 = load i8, i8* %mask, align 1
%tobool = icmp ne i8 %1, 0
br i1 %tobool, label %if.then, label %if.end
if.then: ; preds = %entry
%2 = load i8, i8* %mask, align 1
%conv = zext i8 %2 to i16
%neg = xor i16 %conv, -1
%3 = load volatile i8, i8* inttoptr (i16 36 to i8*), align 1
%conv1 = zext i8 %3 to i16
%and = and i16 %conv1, %neg
%conv2 = trunc i16 %and to i8
store volatile i8 %conv2, i8* inttoptr (i16 36 to i8*), align 1
br label %if.end
if.end: ; preds = %if.then, %entry
ret void
}
; Function Attrs: noinline nounwind optnone
define void @_pinMode(i8 zeroext %pin, i1 zeroext %write) #0 {
entry:
%pin.addr = alloca i8, align 1
%write.addr = alloca i8, align 1
store i8 %pin, i8* %pin.addr, align 1
%frombool = zext i1 %write to i8
store i8 %frombool, i8* %write.addr, align 1
%0 = load i8, i8* %write.addr, align 1
%tobool = trunc i8 %0 to i1
br i1 %tobool, label %if.then, label %if.else3
if.then: ; preds = %entry
%1 = load i8, i8* %pin.addr, align 1
%conv = zext i8 %1 to i16
%cmp = icmp slt i16 %conv, 8
br i1 %cmp, label %if.then2, label %if.else
if.then2: ; preds = %if.then
%2 = load i8, i8* %pin.addr, align 1
call void @enablePortDWrite(i8 zeroext %2)
br label %if.end
if.else: ; preds = %if.then
%3 = load i8, i8* %pin.addr, align 1
call void @enablePortBWrite(i8 zeroext %3)
br label %if.end
if.end: ; preds = %if.else, %if.then2
br label %if.end10
if.else3: ; preds = %entry
%4 = load i8, i8* %pin.addr, align 1
%conv4 = zext i8 %4 to i16
%cmp5 = icmp slt i16 %conv4, 8
br i1 %cmp5, label %if.then7, label %if.else8
if.then7: ; preds = %if.else3
%5 = load i8, i8* %pin.addr, align 1
call void @disablePortDWrite(i8 zeroext %5)
br label %if.end9
if.else8: ; preds = %if.else3
%6 = load i8, i8* %pin.addr, align 1
call void @disablePortBWrite(i8 zeroext %6)
br label %if.end9
if.end9: ; preds = %if.else8, %if.then7
br label %if.end10
if.end10: ; preds = %if.end9, %if.end
ret void
}
attributes #0 = { noinline nounwind optnone "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="atmega328p" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "target-cpu"="atmega328p" "unsafe-fp-math"="false" "use-soft-float"="false" }
!llvm.module.flags = !{!0}
!llvm.ident = !{!1}
!0 = !{i32 1, !"wchar_size", i32 2}
!1 = !{!"clang version 5.0.0 (trunk 307771) (llvm/trunk 307882)"} |
Here's a really reduced testcase target datalayout = "e-p:16:16:16-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-n8"
target triple = "avr-atmel-none"
declare void @_analogReadAsyncTestCallback(i16 zeroext)
; Function Attrs: noinline nounwind optnone
define void @_analogReadAsyncTest(i8 zeroext %pin) {
entry:
call void @_analogReadAsync(i8 zeroext undef, void (i16)* @_analogReadAsyncTestCallback)
ret void
}
declare void @_analogReadAsync(i8 zeroext, void (i16)*) |
Here is a slightly more detailed test case. I've written a small C program that references two types of external symbol, one a variable, one a function. These should produce different fixups with the patched llvm. I ran it through a standard clang (on my Mac) and then a version of llc with the patch from r307888 in it. Attached are the original C, the compiled llvm IR and final asm, showing that the back end works as expected and produces the correct fixups.
I will create a reduced version to make a more comprehensive regression test for the patch. |
Have you used LLVM bugpoint before? If not, let me know and I can write a quick paragraph explaining a way to automatically minimise the testcase |
Or enhance the current one :-) |
FWIW, function pointers have been correctly placed in program memory for a few months now. |
Yes! Amazing! Big change on llvm. :)) |
We can mark this as closed can't we? |
In the attached code I attempt to do a callback from C to a Swift function (actually a Swift lambda but I suspect the result would be the same. I'll check for completeness).
The
_analogReadAsync
function takes a pin and a callback as parameters. When compiling C code that passes a function to this (see the "test" functions), it works. Swift code calling into this fails to execute the callback correctly, with random results.Looking at the LLVM IR produced by swiftc, it looks OK to my amateur eyes:
to set the callback to this function...
This is the assembly emitted...
At first I couldn't see the problem, r22 and r23 are set to 0x032a, which is the address of the callback function. But when this is subsequently passed around and used for an ICALL later, it all goes wrong. Because ICALL expects a program counter value, not an absolute address. And the program counter refers to 16 bit wide addresses. So we should be loading 0x195 into the program counter, 0x01 into r23 and 0x95 into r22.
Compare this to what avr-gcc does...
This test code:
Creates this assembly:
So here you can see, to get to address 0x842, the compiler creates code to load r22/r23 with the program counter value 0x0421, exactly half the address.
avrio.c.txt
main.elf.s.txt
pins.ll.txt
pins.swift.txt
The text was updated successfully, but these errors were encountered: