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 the gcc_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:

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 to gcc_jit_extended_asm_add_output_operand() and gcc_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 extended asm statement with no control flow (i.e. without the goto qualifier).

The parameter asm_template corresponds to the AssemblerTemplate within C’s extended asm 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 extended asm statement that may perform jumps, and use it to terminate the given block. This is equivalent to the goto qualifier in C’s extended asm 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 the GotoLabels parameter within C’s extended asm 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 the asmSymbolicName component of C’s extended asm syntax. It can be NULL. If non-NULL it specifies the symbolic name for the operand.

constraint corresponds to the constraint component of C’s extended asm syntax. It must be non-NULL.

dest corresponds to the cvariablename component of C’s extended asm 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 the asmSymbolicName component of C’s extended asm syntax. It can be NULL. If non-NULL it specifies the symbolic name for the operand.

constraint corresponds to the constraint component of C’s extended asm syntax. It must be non-NULL.

src corresponds to the cexpression component of C’s extended asm 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");