Using Assembly Language with libgccjit¶
libgccjit has some support for directly embedding assembler instructions.
This is based on GCC’s support for inline asm
in C code, and the
following assumes a familiarity with that functionality. See
How to Use Inline Assembly Language in C Code
in GCC’s documentation, the “Extended Asm” section in particular.
Adding assembler instructions within a function¶
-
gcc_jit_extended_asm
¶ A gcc_jit_extended_asm represents an extended
asm
statement: a series of low-level instructions inside a function that convert inputs to outputs.To avoid having an API entrypoint with a very large number of parameters, an extended
asm
statement is made in stages: an initial call to create thegcc_jit_extended_asm
, followed by calls to add operands and set other properties of the statement.There are two API entrypoints for creating a
gcc_jit_extended_asm
:gcc_jit_block_add_extended_asm()
for anasm
statement with no control flow, andgcc_jit_block_end_with_extended_asm_goto()
for anasm goto
.
For example, to create the equivalent of:
asm ("mov %1, %0\n\t" "add $1, %0" : "=r" (dst) : "r" (src));
the following API calls could be used:
gcc_jit_extended_asm *ext_asm = gcc_jit_block_add_extended_asm (block, NULL, "mov %1, %0\n\t" "add $1, %0"); gcc_jit_extended_asm_add_output_operand (ext_asm, NULL, "=r", dst); gcc_jit_extended_asm_add_input_operand (ext_asm, NULL, "r", gcc_jit_lvalue_as_rvalue (src));
Warning
When considering the numbering of operands within an extended
asm
statement (e.g. the%0
and%1
above), the equivalent to the C syntax is followed i.e. all output operands, then all input operands, regardless of what order the calls togcc_jit_extended_asm_add_output_operand()
andgcc_jit_extended_asm_add_input_operand()
were made in.As in the C syntax, operands can be given symbolic names to avoid having to number them. For example, to create the equivalent of:
asm ("bsfl %[aMask], %[aIndex]" : [aIndex] "=r" (Index) : [aMask] "r" (Mask) : "cc");
the following API calls could be used:
gcc_jit_extended_asm *ext_asm = gcc_jit_block_add_extended_asm (block, NULL, "bsfl %[aMask], %[aIndex]"); gcc_jit_extended_asm_add_output_operand (ext_asm, "aIndex", "=r", index); gcc_jit_extended_asm_add_input_operand (ext_asm, "aMask", "r", gcc_jit_param_as_rvalue (mask)); gcc_jit_extended_asm_add_clobber (ext_asm, "cc");
-
gcc_jit_extended_asm *
gcc_jit_block_add_extended_asm
(gcc_jit_block *block, gcc_jit_location *loc, const char *asm_template)¶ Create a
gcc_jit_extended_asm
for an extendedasm
statement with no control flow (i.e. without thegoto
qualifier).The parameter
asm_template
corresponds to the AssemblerTemplate within C’s extendedasm
syntax. It must be non-NULL. The call takes a copy of the underlying string, so it is valid to pass in a pointer to an on-stack buffer.
-
gcc_jit_extended_asm *
gcc_jit_block_end_with_extended_asm_goto
(gcc_jit_block *block, gcc_jit_location *loc, const char *asm_template, int num_goto_blocks, gcc_jit_block **goto_blocks, gcc_jit_block *fallthrough_block)¶ Create a
gcc_jit_extended_asm
for an extendedasm
statement that may perform jumps, and use it to terminate the given block. This is equivalent to thegoto
qualifier in C’s extendedasm
syntax.For example, to create the equivalent of:
asm goto ("btl %1, %0\n\t" "jc %l[carry]" : // No outputs : "r" (p1), "r" (p2) : "cc" : carry);
the following API calls could be used:
const char *asm_template = (use_name ? /* Label referred to by name: "%l[carry]". */ ("btl %1, %0\n\t" "jc %l[carry]") : /* Label referred to numerically: "%l2". */ ("btl %1, %0\n\t" "jc %l2")); gcc_jit_extended_asm *ext_asm = gcc_jit_block_end_with_extended_asm_goto (b_start, NULL, asm_template, 1, &b_carry, b_fallthru); gcc_jit_extended_asm_add_input_operand (ext_asm, NULL, "r", gcc_jit_param_as_rvalue (p1)); gcc_jit_extended_asm_add_input_operand (ext_asm, NULL, "r", gcc_jit_param_as_rvalue (p2)); gcc_jit_extended_asm_add_clobber (ext_asm, "cc");
here referencing a
gcc_jit_block
named “carry”.num_goto_blocks
must be >= 0.goto_blocks
must be non-NULL. This corresponds to theGotoLabels
parameter within C’s extendedasm
syntax. The block names can be referenced within the assembler template.fallthrough_block
can be NULL. If non-NULL, it specifies the block to fall through to after the statement.Note
This is needed since each
gcc_jit_block
must have a single exit point, as a basic block: you can’t jump from the middle of a block. A “goto” is implicitly added after the asm to handle the fallthrough case, which is equivalent to what would have happened in the C case.
-
void
gcc_jit_extended_asm_set_volatile_flag
(gcc_jit_extended_asm *ext_asm, int flag)¶ Set whether the
gcc_jit_extended_asm
has side-effects, equivalent to the volatile qualifier in C’s extended asm syntax.For example, to create the equivalent of:
asm volatile ("rdtsc\n\t" // Returns the time in EDX:EAX. "shl $32, %%rdx\n\t" // Shift the upper bits left. "or %%rdx, %0" // 'Or' in the lower bits. : "=a" (msr) : : "rdx");
the following API calls could be used:
gcc_jit_extended_asm *ext_asm = gcc_jit_block_add_extended_asm (block, NULL, "rdtsc\n\t" /* Returns the time in EDX:EAX. */ "shl $32, %%rdx\n\t" /* Shift the upper bits left. */ "or %%rdx, %0"); /* 'Or' in the lower bits. */ gcc_jit_extended_asm_set_volatile_flag (ext_asm, 1); gcc_jit_extended_asm_add_output_operand (ext_asm, NULL, "=a", msr); gcc_jit_extended_asm_add_clobber (ext_asm, "rdx");
where the
gcc_jit_extended_asm
is flagged as volatile.
-
void
gcc_jit_extended_asm_set_inline_flag
(gcc_jit_extended_asm *ext_asm, int flag)¶ Set the equivalent of the inline qualifier in C’s extended
asm
syntax.
-
void
gcc_jit_extended_asm_add_output_operand
(gcc_jit_extended_asm *ext_asm, const char *asm_symbolic_name, const char *constraint, gcc_jit_lvalue *dest)¶ Add an output operand to the extended
asm
statement. See the Output Operands section of the documentation of the C syntax.asm_symbolic_name
corresponds to theasmSymbolicName
component of C’s extendedasm
syntax. It can be NULL. If non-NULL it specifies the symbolic name for the operand.constraint
corresponds to theconstraint
component of C’s extendedasm
syntax. It must be non-NULL.dest
corresponds to thecvariablename
component of C’s extendedasm
syntax. It must be non-NULL.// Example with a NULL symbolic name, the equivalent of: // : "=r" (dst) gcc_jit_extended_asm_add_output_operand (ext_asm, NULL, "=r", dst); // Example with a symbolic name ("aIndex"), the equivalent of: // : [aIndex] "=r" (index) gcc_jit_extended_asm_add_output_operand (ext_asm, "aIndex", "=r", index);
This function can’t be called on an
asm goto
as such instructions can’t have outputs; see the Goto Labels section of GCC’s “Extended Asm” documentation.
-
void
gcc_jit_extended_asm_add_input_operand
(gcc_jit_extended_asm *ext_asm, const char *asm_symbolic_name, const char *constraint, gcc_jit_rvalue *src)¶ Add an input operand to the extended
asm
statement. See the Input Operands section of the documentation of the C syntax.asm_symbolic_name
corresponds to theasmSymbolicName
component of C’s extendedasm
syntax. It can be NULL. If non-NULL it specifies the symbolic name for the operand.constraint
corresponds to theconstraint
component of C’s extendedasm
syntax. It must be non-NULL.src
corresponds to thecexpression
component of C’s extendedasm
syntax. It must be non-NULL.// Example with a NULL symbolic name, the equivalent of: // : "r" (src) gcc_jit_extended_asm_add_input_operand (ext_asm, NULL, "r", gcc_jit_lvalue_as_rvalue (src)); // Example with a symbolic name ("aMask"), the equivalent of: // : [aMask] "r" (Mask) gcc_jit_extended_asm_add_input_operand (ext_asm, "aMask", "r", gcc_jit_lvalue_as_rvalue (mask));
-
void
gcc_jit_extended_asm_add_clobber
(gcc_jit_extended_asm *ext_asm, const char *victim)¶ Add victim to the list of registers clobbered by the extended
asm
statement. It must be non-NULL. See the Clobbers and Scratch Registers section of the documentation of the C syntax.Statements with multiple clobbers will require multiple calls, one per clobber.
For example:
gcc_jit_extended_asm_add_clobber (ext_asm, "r0"); gcc_jit_extended_asm_add_clobber (ext_asm, "cc"); gcc_jit_extended_asm_add_clobber (ext_asm, "memory");
A gcc_jit_extended_asm
is a gcc_jit_object
“owned” by
the block’s context. The following upcast is available:
-
gcc_jit_object *
gcc_jit_extended_asm_as_object
(gcc_jit_extended_asm *ext_asm)¶ Upcast from extended
asm
to object.
Adding top-level assembler statements¶
In addition to creating extended asm
instructions within a function,
there is support for creating “top-level” assembler statements, outside
of any function.
-
void
gcc_jit_context_add_top_level_asm
(gcc_jit_context *ctxt, const char *asm_stmts)¶ Create a set of top-level asm statements, analogous to those created by GCC’s “basic”
asm
syntax in C at file scope.For example, to create the equivalent of:
asm ("\t.pushsection .text\n" "\t.globl add_asm\n" "\t.type add_asm, @function\n" "add_asm:\n" "\tmovq %rdi, %rax\n" "\tadd %rsi, %rax\n" "\tret\n" "\t.popsection\n");
the following API calls could be used:
gcc_jit_context_add_top_level_asm (ctxt, "\t.pushsection .text\n" "\t.globl add_asm\n" "\t.type add_asm, @function\n" "add_asm:\n" "\tmovq %rdi, %rax\n" "\tadd %rsi, %rax\n" "\tret\n" "\t# some asm here\n" "\t.popsection\n");