Skip to content

Indirect call address is possibly being loaded incorrectly #65

Closed
@carlos4242

Description

@carlos4242

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:

define void @_TF3AVR15analogReadAsyncFT3pinVs5UInt88callbackcVs6UInt16T__T_(i8, i8*) #2 {
entry:
  %2 = call zeroext i1 @_analogReadAsync(i8 zeroext %0, void (i16)* @_TToFF3AVR15analogReadAsyncFT3pinVs5UInt88callbackcVs6UInt16T__T_U_FS1_T_)
  ret void
}

declare zeroext i1 @_analogReadAsync(i8 zeroext, void (i16)*) #1

to set the callback to this function...

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
}

This is the assembly emitted...

000002fc <_TF3AVR15analogReadAsyncFT3pinVs5UInt88callbackcVs6UInt16T__T_>:
     2fc:	cf 93       	push	r28
     2fe:	df 93       	push	r29
     300:	cd b7       	in	r28, 0x3d	; 61
     302:	de b7       	in	r29, 0x3e	; 62
     304:	6a e2       	ldi	r22, 0x2A	; 42
     306:	73 e0       	ldi	r23, 0x03	; 3
     308:	0e 94 8f 05 	call	0xb1e	; 0xb1e <_analogReadAsync>
     30c:	df 91       	pop	r29
     30e:	cf 91       	pop	r28
     310:	08 95       	ret


0000032a <_TToFF3AVR15analogReadAsyncFT3pinVs5UInt88callbackcVs6UInt16T__T_U_FS1_T_>:
     32a:	cf 93       	push	r28
     32c:	df 93       	push	r29
     32e:	cd b7       	in	r28, 0x3d	; 61
     330:	de b7       	in	r29, 0x3e	; 62
     332:	0e 94 89 01 	call	0x312	; 0x312 <_TFF3AVR15analogReadAsyncFT3pinVs5UInt88callbackcVs6UInt16T__T_U_FS1_T_>
     336:	df 91       	pop	r29
     338:	cf 91       	pop	r28
     33a:	08 95       	ret

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:

void _analogReadAsyncTestCallback(unsigned short value) {
	_digitalWrite(13, true);
}

void _analogReadAsyncTest(unsigned char pin) {
	_analogReadAsync(pin,_analogReadAsyncTestCallback);
}

Creates this assembly:

00000b8c <_analogReadAsyncTest>:
     b8c:	61 e2       	ldi	r22, 0x21	; 33
     b8e:	74 e0       	ldi	r23, 0x04	; 4
     b90:	0c 94 8f 05 	jmp	0xb1e	; 0xb1e <_analogReadAsync>

00000842 <_analogReadAsyncTestCallback>:
     842:	61 e0       	ldi	r22, 0x01	; 1
     844:	8d e0       	ldi	r24, 0x0D	; 13
     846:	0c 94 9c 02 	jmp	0x538	; 0x538 <_digitalWrite>

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions