romell
Posts: 25
Joined: Mon Jul 23, 2012 6:57 pm

Integer division

Tue Jul 24, 2012 11:47 am

Hi,

When doing any kind of integer division, I'm getting the linker error:

Code: Select all

undefined reference to `__aeabi_idiv'
I tried this both with the pre-built toolchain from: launchpad.net/gcc-arm-embedded and with a gcc/newlib toolchain built with crosstool-ng...

My current workaround is casting to float and doing the division in hardware:

Code: Select all

(int)((float)a / (float)b)
which seems to work, but is very ugly.

Does anyone know how to get integer division working with gcc?

User avatar
Cycl0ne
Posts: 102
Joined: Mon Jun 25, 2012 8:03 am

Re: Integer division

Tue Jul 24, 2012 1:39 pm

its a builtin gcc function. you need libgcc.a or code them yourself.

the arm cpu doesnt have % and / in the core as commands, so they have to be emulated

Heres my ripped code ;) What you need then is a __DIV0 Function,
void __DIV0() {}
to catch the Division through 0

Code: Select all

.macro ARM_DIV_BODY dividend, divisor, result, curbit

#if __LINUX_ARM_ARCH__ >= 5

	clz	\curbit, \divisor
	clz	\result, \dividend
	sub	\result, \curbit, \result
	mov	\curbit, #1
	mov	\divisor, \divisor, lsl \result
	mov	\curbit, \curbit, lsl \result
	mov	\result, #0
	@ Division loop
1:	cmp	\dividend, \divisor
	subhs	\dividend, \dividend, \divisor
	orrhs	\result,   \result,   \curbit
	cmp	\dividend, \divisor,  lsr #1
	subhs	\dividend, \dividend, \divisor, lsr #1
	orrhs	\result,   \result,   \curbit,  lsr #1
	cmp	\dividend, \divisor,  lsr #2
	subhs	\dividend, \dividend, \divisor, lsr #2
	orrhs	\result,   \result,   \curbit,  lsr #2
	cmp	\dividend, \divisor,  lsr #3
	subhs	\dividend, \dividend, \divisor, lsr #3
	orrhs	\result,   \result,   \curbit,  lsr #3
	cmp	\dividend, #0			@ Early termination?
	movnes	\curbit,   \curbit,  lsr #4	@ No, any more bits to do?
	movne	\divisor,  \divisor, lsr #4
	bne	1b
.endm

.macro ARM_DIV2_ORDER divisor, order
	clz	\order, \divisor
	rsb	\order, \order, #31
.endm

	.align	5
.globl __divsi3
.globl __aeabi_idiv
__divsi3:
__aeabi_idiv:
	cmp	r1, #0
	eor	ip, r0, r1			@ save the sign of the result.
	beq	Ldiv0
	rsbmi	r1, r1, #0			@ loops below use unsigned.
	subs	r2, r1, #1			@ division by 1 or -1 ?
	beq	10f
	movs	r3, r0
	rsbmi	r3, r0, #0			@ positive dividend value
	cmp	r3, r1
	bls	11f
	tst	r1, r2				@ divisor is power of 2 ?
	beq	12f

	ARM_DIV_BODY r3, r1, r0, r2

	cmp	ip, #0
	rsbmi	r0, r0, #0
	mov	pc, lr

10:	teq	ip, r0				@ same sign ?
	rsbmi	r0, r0, #0
	mov	pc, lr

11:	movlo	r0, #0
	moveq	r0, ip, asr #31
	orreq	r0, r0, #1
	mov	pc, lr

12:	ARM_DIV2_ORDER r1, r2

	cmp	ip, #0
	mov	r0, r3, lsr r2
	rsbmi	r0, r0, #0
	mov	pc, lr

Ldiv0:

	str	lr, [sp, #-4]!
	bl	__div0
	mov	r0, #0			@ About as wrong as it could be.
	ldr	pc, [sp], #4

dwelch67
Posts: 1002
Joined: Sat May 26, 2012 5:32 pm

Re: Integer division

Tue Jul 24, 2012 6:22 pm

this is a gcc library, when stuck with this I (re)build gcc, but first put a typo in the math library file for arm, then it stops with the full command line that gcc is using. I think it builds the same file many times with different defines to create each of the .o files for each of the major functions. this is pretty straight forward to see from the command line. then as mentioned you need to have the divide by zero support which can be just a bx lr unless you care to trap it.

valtonia
Posts: 26
Joined: Wed Jul 04, 2012 9:09 pm

Re: Integer division

Tue Jul 24, 2012 10:11 pm

Alternatively, if you're using the toolchain that's used to build the linux kernel (not sure what it's called), the command will look something like:

Code: Select all

/arm-bcm2708/x86-linux64-cross-arm-linux-hardfp/bin/arm-bcm2708hardfp-linux-gnueabi-gcc
You just need to link the supplied libgcc.a.

Here's my Makefile for my projects (note where libgcc (-lgcc on the LDLIBS line) is included at the END of the link line - this is important; it doesn't work anywhere else)

Code: Select all

PREFIX=/usr/local/arm-bcm2708/x86-linux64-cross-arm-linux-hardfp
BIN=bin/arm-bcm2708hardfp-linux-gnueabi-

RM=rm -f
DD=dd
CC=$(PREFIX)/$(BIN)gcc
AS=$(PREFIX)/$(BIN)as
LD=$(PREFIX)/$(BIN)ld
OBJCOPY=$(PREFIX)/$(BIN)objcopy

ARMFLAGS=-mcpu=arm1176jzf-s -mfpu=vfp -mhard-float
CFLAGS=--std=c99 -O0 -ffreestanding $(ARMFLAGS)
LDFLAGS=-nostdlib
LDLIBS=-L$(PREFIX)/lib/gcc/arm-bcm2708hardfp-linux-gnueabi/4.5.1 -lgcc

SRC_DIR=.
CSRCS=$(foreach sdir,$(SRC_DIR),$(wildcard *.c))
COBJS=$(CSRCS:.c=.o)
ASRCS=$(foreach sdir,$(SRC_DIR),$(wildcard *.s))
AOBJS=$(ASRCS:.s=.o)

vpath %.c $(SRC_DIR)
vpath %.s $(SRC_DIR)

%.o: %.c
        $(CC) $(CFLAGS) -c -o $*.o $<

%.o: %.s
        $(CC) $(CFLAGS) -c -o $*.o $<

OBJ = $(AOBJS) $(COBJS)

kernel.bin: kernel.elf
        $(OBJCOPY) -O binary $< $@

kernel.elf: kernel.ld $(OBJ)
        $(LD) $(LDFLAGS) -T kernel.ld $(OBJ) -o $@ $(LDLIBS)

all: kernel.bin

clean:
        $(RM) $(OBJ)
V.

romell
Posts: 25
Joined: Mon Jul 23, 2012 6:57 pm

Re: Integer division

Wed Jul 25, 2012 2:08 pm

Thanks for the answers. It turned out that I had simply put my -lgcc directive at the wrong place in my ld arguments. Didn't know that it what so picky about the placement...

Anyway, I also found the linux kernel implementation of the division functions in the file arch/arm/lib/lib1funcs.S. Seems to be pretty efficient.

Return to “Bare metal, Assembly language”