Our example is done, but you may be wondering about exactly how the compiler turned what we gave it into the machine code seen above.
We can examine what the compiler is doing in detail by setting:
gcc_jit_context_set_bool_option (state.ctxt, GCC_JIT_BOOL_OPTION_DUMP_EVERYTHING, 1); gcc_jit_context_set_bool_option (state.ctxt, GCC_JIT_BOOL_OPTION_KEEP_INTERMEDIATES, 1);
This will dump detailed information about the compiler’s state to a
directory under /tmp
, and keep it from being cleaned up.
The precise names and their formats of these files is subject to change. Higher optimization levels lead to more files. Here’s what I saw (edited for brevity; there were almost 200 files):
intermediate files written to /tmp/libgccjit-KPQbGw $ ls /tmp/libgccjit-KPQbGw/ fake.c.000i.cgraph fake.c.000i.type-inheritance fake.c.004t.gimple fake.c.007t.omplower fake.c.008t.lower fake.c.011t.eh fake.c.012t.cfg fake.c.014i.visibility fake.c.015i.early_local_cleanups fake.c.016t.ssa # etc
The gimple code is converted into Static Single Assignment form, with annotations for use when generating the debuginfo:
$ less /tmp/libgccjit-KPQbGw/fake.c.016t.ssa
;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) factorial (signed int arg) { signed int stack[8]; signed int stack_depth; signed int x; signed int y; <unnamed type> _20; signed int _21; signed int _38; signed int _44; signed int _51; signed int _56; initial: stack_depth_3 = 0; # DEBUG stack_depth => stack_depth_3 stack[stack_depth_3] = arg_5(D); stack_depth_7 = stack_depth_3 + 1; # DEBUG stack_depth => stack_depth_7 # DEBUG instr0 => NULL # DEBUG /* DUP */ => NULL stack_depth_8 = stack_depth_7 + -1; # DEBUG stack_depth => stack_depth_8 x_9 = stack[stack_depth_8]; # DEBUG x => x_9 stack[stack_depth_8] = x_9; stack_depth_11 = stack_depth_8 + 1; # DEBUG stack_depth => stack_depth_11 stack[stack_depth_11] = x_9; stack_depth_13 = stack_depth_11 + 1; # DEBUG stack_depth => stack_depth_13 # DEBUG instr1 => NULL # DEBUG /* PUSH_CONST */ => NULL stack[stack_depth_13] = 2; /* etc; edited for brevity */
We can perhaps better see the code by turning off
GCC_JIT_BOOL_OPTION_DEBUGINFO to suppress all those DEBUG
statements, giving:
$ less /tmp/libgccjit-1Hywc0/fake.c.016t.ssa
;; Function factorial (factorial, funcdef_no=0, decl_uid=53, symbol_order=0) factorial (signed int arg) { signed int stack[8]; signed int stack_depth; signed int x; signed int y; <unnamed type> _20; signed int _21; signed int _38; signed int _44; signed int _51; signed int _56; initial: stack_depth_3 = 0; stack[stack_depth_3] = arg_5(D); stack_depth_7 = stack_depth_3 + 1; stack_depth_8 = stack_depth_7 + -1; x_9 = stack[stack_depth_8]; stack[stack_depth_8] = x_9; stack_depth_11 = stack_depth_8 + 1; stack[stack_depth_11] = x_9; stack_depth_13 = stack_depth_11 + 1; stack[stack_depth_13] = 2; stack_depth_15 = stack_depth_13 + 1; stack_depth_16 = stack_depth_15 + -1; y_17 = stack[stack_depth_16]; stack_depth_18 = stack_depth_16 + -1; x_19 = stack[stack_depth_18]; _20 = x_19 < y_17; _21 = (signed int) _20; stack[stack_depth_18] = _21; stack_depth_23 = stack_depth_18 + 1; stack_depth_24 = stack_depth_23 + -1; x_25 = stack[stack_depth_24]; if (x_25 != 0) goto <bb 4> (instr9); else goto <bb 3> (instr4); instr4: /* DUP */: stack_depth_26 = stack_depth_24 + -1; x_27 = stack[stack_depth_26]; stack[stack_depth_26] = x_27; stack_depth_29 = stack_depth_26 + 1; stack[stack_depth_29] = x_27; stack_depth_31 = stack_depth_29 + 1; stack[stack_depth_31] = 1; stack_depth_33 = stack_depth_31 + 1; stack_depth_34 = stack_depth_33 + -1; y_35 = stack[stack_depth_34]; stack_depth_36 = stack_depth_34 + -1; x_37 = stack[stack_depth_36]; _38 = x_37 - y_35; stack[stack_depth_36] = _38; stack_depth_40 = stack_depth_36 + 1; stack_depth_41 = stack_depth_40 + -1; x_42 = stack[stack_depth_41]; _44 = factorial (x_42); stack[stack_depth_41] = _44; stack_depth_46 = stack_depth_41 + 1; stack_depth_47 = stack_depth_46 + -1; y_48 = stack[stack_depth_47]; stack_depth_49 = stack_depth_47 + -1; x_50 = stack[stack_depth_49]; _51 = x_50 * y_48; stack[stack_depth_49] = _51; stack_depth_53 = stack_depth_49 + 1; # stack_depth_1 = PHI <stack_depth_24(2), stack_depth_53(3)> instr9: /* RETURN */: stack_depth_54 = stack_depth_1 + -1; x_55 = stack[stack_depth_54]; _56 = x_55; stack ={v} {CLOBBER}; return _56; }
Note in the above how all the gcc_jit_block instances we
created have been consolidated into just 3 blocks in GCC’s internal
representation: initial
, instr4
and instr9
.