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¶
-
class
gccjit::extended_asm¶ A gccjit::extended_asm represents an extended
asmstatement: a series of low-level instructions inside a function that convert inputs to outputs.gccjit::extended_asmis a subclass ofgccjit::object. It is a thin wrapper around the C API’sgcc_jit_extended_asm *.To avoid having an API entrypoint with a very large number of parameters, an extended
asmstatement 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 anasmstatement 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
asmstatement (e.g. the%0and%1above), 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_asmfor an extendedasmstatement with no control flow (i.e. without thegotoqualifier).The parameter
asm_templatecorresponds to the AssemblerTemplate within C’s extendedasmsyntax. 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_asmfor an extendedasmstatement that may perform jumps, and use it to terminate the given block. This is equivalent to thegotoqualifier in C’s extendedasmsyntax.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_blocknamed “carry”.num_goto_blockscorresponds to theGotoLabelsparameter within C’s extendedasmsyntax. The block names can be referenced within the assembler template.fallthrough_blockcan be NULL. If non-NULL, it specifies the block to fall through to after the statement.Note
This is needed since each
gccjit::blockmust 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_asmhas 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_asmis 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
asmsyntax.
-
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
asmstatement. See the Output Operands section of the documentation of the C syntax.asm_symbolic_namecorresponds to theasmSymbolicNamecomponent of C’s extendedasmsyntax, and specifies the symbolic name for the operand. See the overload below for an alternative that does not supply a symbolic name.constraintcorresponds to theconstraintcomponent of C’s extendedasmsyntax.destcorresponds to thecvariablenamecomponent of C’s extendedasmsyntax.// 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 gotoas 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
asmstatement. See the Input Operands section of the documentation of the C syntax.asm_symbolic_namecorresponds to theasmSymbolicNamecomponent of C’s extendedasmsyntax. See the overload below for an alternative that does not supply a symbolic name.constraintcorresponds to theconstraintcomponent of C’s extendedasmsyntax.srccorresponds to thecexpressioncomponent of C’s extendedasmsyntax.// 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
asmstatement. 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)¶ Create a set of top-level asm statements, analogous to those created by GCC’s “basic”
asmsyntax 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");