Skip to content

Add a custom instruction ​

A custom instruction starts as an ordinary function import. Wago replaces calls to that import while compiling the guest.

Pick the guest ABI ​

This WAT import carries two four-bit values through normal i32 parameters:

wat
(import "wago:instr/example.int" "i4.add"
  (func $i4.add (param i32 i32) (result i32)))

Any language that can emit the same import can use it.

Request the module ​

Request compiler.instruction.define with wago:instr/example.int in Scope.Modules.

Define its meaning ​

Inside Register, get Registrar.CompilerInstructions, then define the logical widths and portable handler:

go
instructions, err := reg.CompilerInstructions()
if err != nil {
    return err
}
return instructions.Define(wago.InstructionSpec{
    Module: "wago:instr/example.int",
    Name:   "i4.add",
    Input:  []int32{4, 4},
    Output: []int32{4},
    Handler: func(_ wago.InstructionContext, in []wago.Bits) ([]wago.Bits, error) {
        value, err := wago.BitsFromUint32(4, in[0].Uint32()+in[1].Uint32())
        return []wago.Bits{value}, err
    },
})

The handler is the fallback and the executable definition of the operation.

Add a scalar lowering ​

Add this Lower field to the same InstructionSpec:

go
Lower: func(ctx wago.LoweringContext) error {
	ctx.Output(0, ctx.Add(ctx.Input(0), ctx.Input(1)))
	return nil
},

Wago can inline this recipe on supported backends. The portable handler remains available elsewhere.

Use target-specific code generation only when the scalar recipe cannot express the operation. Raw AMD64 or ARM64 emission is trusted backend code and needs architecture-tagged files.

Run examples/18-custom-instruction for the complete plugin and WAT guest.

Run the published example without a source checkout:

sh
go run github.com/wago-org/wago/examples/18-custom-instruction@b084a7c9343f81a9120ca80a13d133884e88d514

Released under the Apache 2.0 License.