title How GCC adds, for a Motorola 6809 user strick ip 12.38.208.106 vol 1 lock ******** /tt /b /big /big http://wiki.yak.net/1166 Here's a short C program "zadd.c" /box( /pre( int count; void Increment(int addend) { count = count + addend; } /pre) /box) We have a 15 year old compiler (circa 2007) for the Motorola 6809 8-bit processor (circa 1977). Let's compile that file into assembly: /tt /b $ gcc6809 -O2 -S zadd.c /box( /pre( ;;; gcc for m6809 : Nov 22 2019 00:09:58 ;;; 4.6.4 (gcc6809lw pl6) ;;; ABI version 1 ;;; -mint16 .module zadd.c .area .text .globl _Increment _Increment: pshs u leau ,s ldd _count leax d,x stx _count puls u,pc .area .bss .globl _count _count: .blkb 2 /pre) /box) For the function Increment, removing the preamble (pshs, leau) and the postamble (puls), this is the guts of the function. /box( /pre( ldd _count leax d,x stx _count /pre) /box) About this compiler and the 6809: * "int" is 16 bits (that is, 2 bytes). * The 6809 contains (among other things) five 16-bit registers: D, X, Y, U, S. (S is the stack pointer. Usually U is the frame pointer.) * In this compiler, the first 16-bit argument arrives in the X register (and a 16-bit result would be returned in the X register). Let's see how GCC created that. First it builds a list of abstract (architecture-neutral) instructions: /tt /b $ gcc6809 -O2 -fdump-rtl-expand -S zadd.c GCC's code generation rules are written in this pseudo-lisp language called RTL. Nontrivial items from /tt zadd.c.144r.expand: /box( /pre( (insn 2 4 3 2 (set (reg/v:HI 30 [ addend ]) (reg:HI 1 x [ addend ])) zadd.c:3 -1 (nil)) (insn 6 5 7 3 (set (mem/c/i:HI (symbol_ref:HI ("count") ) [2 count+0 S2 A8]) (plus:HI (mem/c/i:HI (symbol_ref:HI ("count") ) [2 count+0 S2 A8]) (reg/v:HI 30 [ addend ]))) zadd.c:4 -1 (nil)) /pre) /box) Here is a much-simplified version: /box( /pre( (set (reg 30) (reg 1 "x") ; LET reg30 = reg"x" (set (mem "count") (plus (mem "count") (reg 30)) ; LET count = count + reg30 /pre) /box) If use the /tt /b -dP option, it'll put the RTL as comments in the assembly, and identify exactly which rules generated them. /tt /b $ gcc6809 -O2 -fdump-rtl-expand -dP -S zadd.c /box( /pre( ; (insn 11 3 6 (set (reg:HI 6 d) ; (mem/c/i:HI (symbol_ref:HI ("count") ) [2 count+0 S2 A8])) zadd.c:4 10 {*movhi_1} ; (nil)) ldd _count ; 11 *movhi_1/4 [length = 4] ; (insn 6 11 10 (set (reg:HI 1 x) ; (plus:HI (reg:HI 6 d) ; (reg/v:HI 1 x [orig:30 addend ] [30]))) zadd.c:4 26 {addhi3} ; (expr_list:REG_DEAD (reg:HI 6 d) ; (nil))) leax d,x ; 6 addhi3/4 [length = 4] ; (insn 10 6 15 (set (mem/c/i:HI (symbol_ref:HI ("count") ) [2 count+0 S2 A8]) ; (reg:HI 1 x)) zadd.c:4 10 {*movhi_1} ; (expr_list:REG_DEAD (reg:HI 1 x) ; (nil))) stx _count ; 10 *movhi_1/5 [length = 4] /pre) /box) The most interesting instruction is the middle one, "leax d,x", formed by a rule named addhi3 with the constraint alternative 4. Here's the definition of addh3. The constraints have 8 alternatives /tt /b gitlab.com/dfffffff/gcc6809/gcc/config/m6809/m6809.md /box( /pre( (define_insn "addhi3" [(set (match_operand:HI 0 "nonimmediate_operand" "=d, d, d, a,?a, a,???T,a") (plus:HI(match_operand:HI 1 "add_general_operand" "%0, 0, 0, d, 0, a, 0, a") (match_operand:HI 2 "general_operand" " 0, !U, mi, a, m, d, T, i") ))] "" "@ lslb\t\t;addhi: R:%0 += R:%2\;rola\t\t;also R:%0 *= 2 pshs\t%2\t;addhi: R:%0 += R:%2\;add%0\t,s++ add%0\t%2 lea%0\t%1,%2 # lea%0\t%2,%1 # lea%0\t%a2,%1" [(set_attr "length" "2,6,*,*,7,*,7,*")]) /pre) /box) The 4th set of constraints /tt /b a, d, a, (read vertically) causes the 4th line after the "@" to be emitted: /tt /b lea%0\t%1,%2 and expanding /tt %0=`x` ; %1=`d` ; %2=`x` that becomes /tt /b leax d,x / / / / / / / / / / / / / /