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.
These entrypoints were added in LIBGCCJIT_ABI_15; you can test for their presence using
#ifdef LIBGCCJIT_HAVE_ASM_STATEMENTS
Adding assembler instructions within a function#
-
class gccjit::extended_asm#
A gccjit::extended_asm represents an extended
asm
statement: a series of low-level instructions inside a function that convert inputs to outputs.gccjit::extended_asm
is a subclass ofgccjit::object
. It is a thin wrapper around the C API’s gcc_jit_extended_asm*.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 thegccjit::extended_asm
, followed by calls to add operands and set other properties of the statement.There are two API entrypoints for creating a
gccjit::extended_asm
:gccjit::block::add_extended_asm()
for anasm
statement with no control flow, andgccjit::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:
block.add_extended_asm ("mov %1, %0\n\t" "add $1, %0") .add_output_operand ("=r", dst) .add_input_operand ("r", 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 togccjit::extended_asm::add_output_operand()
andgccjit::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:
block.add_extended_asm ("bsfl %[aMask], %[aIndex]") .add_output_operand ("aIndex", "=r", index) .add_input_operand ("aMask", "r", mask) .add_clobber ("cc");
-
extended_asm gccjit::block::add_extended_asm(const std::string &asm_template, gccjit::location loc = location())#
Create a
gccjit::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.
-
extended_asm gccjit::block::end_with_extended_asm_goto(const std::string &asm_template, std::vector<block> goto_blocks, block *fallthrough_block, location loc = location())#
Create a
gccjit::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")); std::vector<gccjit::block> goto_blocks ({b_carry}); gccjit::extended_asm ext_asm = (b_start.end_with_extended_asm_goto (asm_template, goto_blocks, &b_fallthru) .add_input_operand ("r", p1) .add_input_operand ("r", p2) .add_clobber ("cc"));
here referencing a
gcc_jit_block
named “carry”.num_goto_blocks
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
gccjit::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.
-
gccjit::extended_asm &gccjit::extended_asm::set_volatile_flag(bool flag)#
Set whether the
gccjit::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:
gccjit::extended_asm ext_asm = block.add_extended_asm ("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. */ .set_volatile_flag (true) .add_output_operand ("=a", msr) .add_clobber ("rdx");
where the
gccjit::extended_asm
is flagged as volatile.
-
gccjit::extended_asm &gccjit::extended_asm::set_inline_flag(bool flag)#
Set the equivalent of the inline qualifier in C’s extended
asm
syntax.
-
gccjit::extended_asm &gccjit::extended_asm::add_output_operand(const std::string &asm_symbolic_name, const std::string &constraint, gccjit::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, and specifies the symbolic name for the operand. See the overload below for an alternative that does not supply a symbolic name.constraint
corresponds to theconstraint
component of C’s extendedasm
syntax.dest
corresponds to thecvariablename
component of C’s extendedasm
syntax.// Example with a symbolic name ("aIndex"), the equivalent of: // : [aIndex] "=r" (index) ext_asm.add_output_operand ("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.
-
gccjit::extended_asm &gccjit::extended_asm::add_output_operand(const std::string &constraint, gccjit::lvalue dest)#
As above, but don’t supply a symbolic name for the operand.
// Example without a symbolic name, the equivalent of: // : "=r" (dst) ext_asm.add_output_operand ("=r", dst);
-
gccjit::extended_asm &gccjit::extended_asm::add_input_operand(const std::string &asm_symbolic_name, const std::string &constraint, gccjit::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. See the overload below for an alternative that does not supply a symbolic name.constraint
corresponds to theconstraint
component of C’s extendedasm
syntax.src
corresponds to thecexpression
component of C’s extendedasm
syntax.// Example with a symbolic name ("aMask"), the equivalent of: // : [aMask] "r" (Mask) ext_asm.add_input_operand ("aMask", "r", mask);
-
gccjit::extended_asm &gccjit::extended_asm::add_input_operand(const std::string &constraint, gccjit::rvalue src)#
As above, but don’t supply a symbolic name for the operand.
// Example without a symbolic name, the equivalent of: // : "r" (src) ext_asm.add_input_operand ("r", src);
-
gccjit::extended_asm &gccjit::extended_asm::add_clobber(const std::string &victim)#
Add victim to the list of registers clobbered by the extended
asm
statement. 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:
ext_asm.add_clobber ("r0").add_clobber ("cc").add_clobber ("memory");
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 gccjit::context::add_top_level_asm(const char *asm_stmts, gccjit::location loc = location())#
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:
ctxt.add_top_level_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# some asm here\n" "\t.popsection\n");