Efficient C for ARM
Data Drive
Examples
The following routine maps a name to a number:
int nameToNumber(const char *name) { if (strcmp(name, "John") == 0) return 5; else if (strcmp(name, "Paul") == 0) return 2; else if (strcmp(name, "George") == 0) return 9; else if (strcmp(name, "Ringo") == 0) return 3; else return -1; /* default case */ }
Because each case is written out individually, the compiler will emit code for every individual case:
nameToNumber
STMFD sp!,{r4,lr}
MOV r4,r0 ; name
ADR r1,|L1.104| ; "John"
BL strcmp
CMP r0,#0 ; yes?
MOVEQ r0,#5 ; retval
LDMEQFD sp!,{r4,pc} ; return
MOV r0,r4 ; name
ADR r1,|L1.112| ; "Paul"
BL strcmp
CMP r0,#0 ; yes?
MOVEQ r0,#2 ; retval
LDMEQFD sp!,{r4,pc} ; return
; ... more for each case ...
26 instructions × 4 bytes = 104 bytes.
If we generalise the code, storing the data in a table which maps the input names to output numbers then we can save a significant amount of code:
#define NELEMS(a) ((int) (sizeof(a) / sizeof(a[0]))) int nameToNumber2(const char *name) { static const struct { const char name[7]; /* NB. PIC */ int value; } map[] = { { "John", 5 }, { "Paul", 2 }, { "George", 9 }, { "Ringo", 3 } }; int i; for (i = 0; i < NELEMS(map); i++) if (strcmp(name, map[i].name) == 0) return map[i].value; return -1; /* default case */ }
nameToNumber2 STMFD sp!,{r4-r6,lr} LDR r6,=mapaddr ; address of 'map' MOV r5,r0 ; stash copy of name MOV r4,#0 ; i loop ADD r0,r4,r4,LSL #1 ; ADD r1,r6,r0,LSL #2 ; map + i*12 MOV r0,r5 ; = name BL strcmp CMP r0,#0 ; match? ADDEQ r0,r4,r4,LSL #1 ; yes ADDEQ r0,r6,r0,LSL #2 ; map + i*12 again LDREQ r0,[r0,#8] ; fetch map[i].value LDMEQFD sp!,{r4-r6,pc} ; return it ADD r4,r4,#1 ; otherwise no match CMP r4,#4 ; loop until out.. BLT loop ; ..of data MVN r0,#0 LDMFD sp!,{r4-r6,pc} ; return -1
18 instructions × 4 bytes = 72 bytes.

