commit 5f7afa1ded17f2d15a63ca6031de148bdf6828fc
parent 4e8750cc367025b506861bcd0d84bfbf80a3daf4
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 13 May 2026 07:25:06 -0700
Expand toy CG API coverage
Diffstat:
8 files changed, 328 insertions(+), 35 deletions(-)
diff --git a/doc/cg-api-status.md b/doc/cg-api-status.md
@@ -42,17 +42,21 @@ Types:
Toy currently supports:
- Immutable and mutable globals, locals, parameters, function calls, recursion,
- pointers, address/deref syntax, arithmetic, comparisons, bitwise operators,
- shifts, unary operators, `&&`, `||`, `while`, `break`, `continue`,
- `if` / `else`, and `return tail f(...)`.
+ variadic functions, `va_list`, pointers, address/deref syntax, arithmetic,
+ comparisons, bitwise operators, shifts, unary operators, `&&`, `||`, `while`,
+ `break`, `continue`, `if` / `else`, and `return tail f(...)`.
- CG API coverage builtins: `typecheck()`, `byteconst()`, `alloca`, `index`,
`memset`, `memcpy`, `atomic_load`, `atomic_store`, `atomic_add`,
`atomic_sub`, `atomic_cas_ok`, `fence`, `popcount`, `ctz`, `clz`, `bswap`,
- `expect`, `fieldtest()`, and `asmnop()`.
+ `expect`, `fieldtest()`, `target()`, `target_os()`, `va_start`, `va_arg`,
+ `va_end`, `va_copy`, `asm(...)`, and `asm_int(...)`.
- Lowering uses the explicit value-category API:
`push_symbol + indirect + load/store`, `push_bytes + indirect + load`,
- `cfree_cg_field`, statement-like `store`, terminator tail calls, and the
- public inline `if` / `else` helpers.
+ `cfree_cg_field`, `cfree_cg_va_*`, `cfree_cg_inline_asm`, statement-like
+ `store`, terminator tail calls, and the public inline `if` / `else` helpers.
+ `asm(arch("aa64", "x64", "rv64"))` chooses a target-specific template at
+ compile time; an empty selected template is a no-op so unsupported inline-asm
+ backends can still compile the same toy source.
Toy validation:
@@ -62,8 +66,10 @@ Toy validation:
- `X`: opt-in Linux cross-target compile/link/execute for `aa64`, `x64`, and
`rv64` via `cfree cc -target`, `cfree ld`, and `test/lib/exec_target.sh`
- Cross-arch validation intentionally has no cross-arch JIT path.
-- `asmnop()` is still AArch64-specific, so x64/rv64 cross runs skip cases that
- use it until toy has target-selectable inline asm.
+- `test/toy/cases/19_cg_api_variadic_asm.toy` executes variadic API coverage on
+ non-macOS targets. On macOS/AArch64 it compiles the same variadic helper but
+ avoids executing it because the current AArch64 backend va_arg walker is still
+ AAPCS64-shaped while Apple `va_list` is a byte cursor.
Current validation:
@@ -71,8 +77,8 @@ Current validation:
- `make bin`
- `make test-cg-api`
- `make test-cg-binder`
-- `make test-toy` - 36 pass, 0 fail, 0 skip
-- `CFREE_TEST_PATHS=X test/toy/run.sh` - 52 pass, 0 fail, 2 skip
+- `make test-toy` - 38 pass, 0 fail, 0 skip
+- `CFREE_TEST_PATHS=X test/toy/run.sh` - 57 pass, 0 fail, 0 skip
- `make test-cg` - 1573 pass, 0 fail, 0 skip
- `test/toy/demo.toy` compiles with `cfree cc -c`
@@ -85,22 +91,12 @@ Current validation:
stale/non-LIFO scopes, invalid field indexes/base types, invalid
`indirect`, and unsupported data relocation widths.
-2. Add toy variadics.
- - Add `va_list` syntax/usage.
- - Add toy coverage for `cfree_cg_va_start`, `cfree_cg_va_arg`,
- `cfree_cg_va_end`, and `cfree_cg_va_copy`.
-
-3. Replace `asmnop()` with general toy inline asm.
- - Add a toy inline-asm surface.
- - Add a compile-time selector/switch mechanism for target properties such as
- `builtin.arch`.
- - Use the selector so toy source can choose target-specific asm templates.
-
-4. Add toy error tests.
+2. Add toy error tests.
- Extend `test/toy/run.sh` with an error-case mode if needed.
- Add expected diagnostic-message matching.
-5. Complete `test/toy/demo.toy`.
+3. Complete `test/toy/demo.toy`.
- The demo currently covers toy syntax, globals, control flow, calls, memory
helpers, atomics, tail calls, inline asm, and public CG API builtins.
- - Add variadic coverage once toy `va_list` support exists.
+ - Add a demo variadic path once macOS/AArch64 va_arg execution matches the
+ public ABI shape.
diff --git a/include/cfree.h b/include/cfree.h
@@ -410,6 +410,7 @@ typedef struct CfreeEnv {
* ============================================================ */
CfreeCompiler* cfree_compiler_new(CfreeTarget, const CfreeEnv*);
void cfree_compiler_free(CfreeCompiler*);
+CfreeTarget cfree_compiler_target(CfreeCompiler*);
/* Resolve a CfreeSrcLoc.file_id to the spelling used when the source was
* registered (typically the path passed to FileIO.read_all, or a memory-
diff --git a/lang/toy/toy.c b/lang/toy/toy.c
@@ -31,7 +31,6 @@
* cfree_cg_push_float
* cfree_cg_push_bytes
* cfree_cg_push_symbol (non-ADDR kinds: PCREL, GOT, PLT, TLS_*)
- * cfree_cg_alloca
*
* Stack:
* cfree_cg_swap
@@ -59,14 +58,8 @@
* cfree_cg_atomic_load / atomic_store / atomic_rmw
* cfree_cg_atomic_cmpxchg / atomic_fence
*
- * Variadics:
- * cfree_cg_va_start / va_arg / va_end / va_copy
- *
* Memory:
* cfree_cg_memcpy / memset / index / field
- *
- * Inline asm:
- * cfree_cg_inline_asm
* ============================================================ */
/* ============================================================
@@ -122,6 +115,7 @@ typedef enum ToyTokenKind {
TOK_BANG,
TOK_DOT,
TOK_DOTSTAR,
+ TOK_DOTDOTDOT,
} ToyTokenKind;
typedef struct ToyToken {
@@ -283,6 +277,11 @@ static ToyToken toy_lexer_next(ToyLexer* lex) {
case '-':
return toy_lexer_emit(lex, TOK_MINUS, start);
case '.':
+ if (lex->cur + 1 < lex->end && lex->cur[0] == '.' &&
+ lex->cur[1] == '.') {
+ lex->cur += 2;
+ return toy_lexer_emit(lex, TOK_DOTDOTDOT, start);
+ }
if (lex->cur < lex->end && *lex->cur == '*') {
lex->cur++;
return toy_lexer_emit(lex, TOK_DOTSTAR, start);
@@ -382,6 +381,7 @@ typedef struct ToyFn {
CfreeCgTypeId ret;
CfreeCgTypeId params[TOY_MAX_PARAMS];
size_t nparams;
+ int variadic;
} ToyFn;
typedef struct ToyGlobal {
@@ -402,8 +402,10 @@ typedef struct ToyParser {
CfreeCgBuiltinTypes types;
CfreeCgTypeId int_type;
CfreeCgTypeId int_ptr_type;
+ CfreeCgTypeId va_list_type;
CfreeCgTypeId pair_type;
CfreeCgTypeId pair_ptr_type;
+ CfreeTarget target;
ToyVar vars[TOY_MAX_VARS];
size_t nvars;
@@ -431,6 +433,8 @@ static void toy_parser_init(ToyParser* p, CfreeCompiler* c, CfreeCg* cg,
p->types = cfree_cg_builtin_types(c);
p->int_type = p->types.isize;
p->int_ptr_type = cfree_cg_type_ptr(c, p->int_type);
+ p->va_list_type = p->types.va_list;
+ p->target = cfree_compiler_target(c);
{
CfreeCgField fields[2];
memset(fields, 0, sizeof fields);
@@ -531,6 +535,11 @@ static CfreeCgTypeId toy_parse_type(ToyParser* p) {
if (toy_parser_match(p, TOK_INT)) {
return p->int_type;
}
+ if (p->cur.kind == TOK_IDENT && p->cur.text_len == 7 &&
+ memcmp(p->cur.text, "va_list", 7) == 0) {
+ toy_parser_advance(p);
+ return p->va_list_type;
+ }
if (toy_parser_match(p, TOK_STAR)) {
CfreeCgTypeId pointee = toy_parse_type(p);
if (pointee == CFREE_CG_TYPE_NONE) {
@@ -578,6 +587,127 @@ static int toy_parse_number_arg(ToyParser* p, int64_t* out) {
return 1;
}
+static int toy_target_selector_index(ToyParser* p) {
+ switch (p->target.arch) {
+ case CFREE_ARCH_ARM_64:
+ return 0;
+ case CFREE_ARCH_X86_64:
+ return 1;
+ case CFREE_ARCH_RV64:
+ return 2;
+ default:
+ return -1;
+ }
+}
+
+static int toy_target_code(ToyParser* p) {
+ switch (p->target.arch) {
+ case CFREE_ARCH_ARM_64:
+ return 1;
+ case CFREE_ARCH_X86_64:
+ return 2;
+ case CFREE_ARCH_RV64:
+ return 3;
+ default:
+ return 0;
+ }
+}
+
+static int toy_parse_string_sym(ToyParser* p, CfreeSym* out, size_t* len_out) {
+ char buf[256];
+ size_t len;
+ if (p->cur.kind != TOK_STRING || p->cur.text_len < 2) {
+ toy_error(p, p->cur.loc, "expected string literal");
+ return 0;
+ }
+ len = p->cur.text_len - 2;
+ if (len >= sizeof buf) {
+ toy_error(p, p->cur.loc, "string literal too long");
+ return 0;
+ }
+ memcpy(buf, p->cur.text + 1, len);
+ buf[len] = '\0';
+ *out = cfree_sym_intern(p->c, buf);
+ if (len_out) *len_out = len;
+ toy_parser_advance(p);
+ return 1;
+}
+
+static int toy_parse_arch_string(ToyParser* p, CfreeSym* out,
+ size_t* len_out) {
+ CfreeSym selected = 0;
+ size_t selected_len = 0;
+ int selected_index = toy_target_selector_index(p);
+
+ if (p->cur.kind == TOK_STRING) return toy_parse_string_sym(p, out, len_out);
+
+ if (p->cur.kind == TOK_IDENT) {
+ CfreeSym name = toy_tok_sym(p, p->cur);
+ if (toy_sym_is(p, name, "arch")) {
+ int i;
+ toy_parser_advance(p);
+ if (!toy_parser_expect(p, TOK_LPAREN)) {
+ toy_error(p, p->cur.loc, "expected '(' after arch");
+ return 0;
+ }
+ for (i = 0; i < 3; ++i) {
+ CfreeSym s;
+ size_t n;
+ if (!toy_parse_string_sym(p, &s, &n)) return 0;
+ if (i == selected_index) {
+ selected = s;
+ selected_len = n;
+ }
+ if (i != 2 && !toy_expect_comma(p)) return 0;
+ }
+ if (!toy_parser_expect(p, TOK_RPAREN)) {
+ toy_error(p, p->cur.loc, "expected ')' after arch selector");
+ return 0;
+ }
+ *out = selected ? selected : cfree_sym_intern(p->c, "");
+ if (len_out) *len_out = selected_len;
+ return 1;
+ }
+ }
+
+ toy_error(p, p->cur.loc, "expected asm template or arch selector");
+ return 0;
+}
+
+static int toy_emit_var_addr(ToyParser* p, CfreeSym name) {
+ ToyVar* v = toy_find_var(p, name);
+ if (v) {
+ cfree_cg_push_local(p->cg, v->slot);
+ cfree_cg_addr(p->cg);
+ return 1;
+ }
+ {
+ ToyGlobal* g = toy_find_global(p, name);
+ if (g) {
+ cfree_cg_push_symbol(p->cg, name, g->type, CFREE_CG_SYMREF_ADDR, 0);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int toy_parse_va_list_addr_arg(ToyParser* p) {
+ CfreeSym name;
+ ToyVar* v;
+ if (p->cur.kind != TOK_IDENT) {
+ toy_error(p, p->cur.loc, "expected va_list identifier");
+ return 0;
+ }
+ name = toy_tok_sym(p, p->cur);
+ v = toy_find_var(p, name);
+ if (!v || v->type != p->va_list_type) {
+ toy_error(p, p->cur.loc, "expected va_list local");
+ return 0;
+ }
+ toy_parser_advance(p);
+ return toy_emit_var_addr(p, name);
+}
+
static CfreeCgTypeId toy_parse_builtin_call(ToyParser* p, CfreeSym name,
int* recognized) {
*recognized = 1;
@@ -741,6 +871,56 @@ static CfreeCgTypeId toy_parse_builtin_call(ToyParser* p, CfreeSym name,
return p->int_type;
}
+ if (toy_sym_is(p, name, "target")) {
+ if (!toy_parser_expect(p, TOK_LPAREN) ||
+ !toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE;
+ cfree_cg_push_int(p->cg, (uint64_t)toy_target_code(p), p->int_type);
+ return p->int_type;
+ }
+
+ if (toy_sym_is(p, name, "target_os")) {
+ if (!toy_parser_expect(p, TOK_LPAREN) ||
+ !toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE;
+ cfree_cg_push_int(p->cg, (uint64_t)p->target.os, p->int_type);
+ return p->int_type;
+ }
+
+ if (toy_sym_is(p, name, "va_start")) {
+ if (!toy_parser_expect(p, TOK_LPAREN) || !toy_parse_va_list_addr_arg(p) ||
+ !toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE;
+ cfree_cg_va_start(p->cg);
+ cfree_cg_push_int(p->cg, 0, p->int_type);
+ return p->int_type;
+ }
+
+ if (toy_sym_is(p, name, "va_end")) {
+ if (!toy_parser_expect(p, TOK_LPAREN) || !toy_parse_va_list_addr_arg(p) ||
+ !toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE;
+ cfree_cg_va_end(p->cg);
+ cfree_cg_push_int(p->cg, 0, p->int_type);
+ return p->int_type;
+ }
+
+ if (toy_sym_is(p, name, "va_copy")) {
+ if (!toy_parser_expect(p, TOK_LPAREN) || !toy_parse_va_list_addr_arg(p) ||
+ !toy_expect_comma(p) || !toy_parse_va_list_addr_arg(p) ||
+ !toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE;
+ cfree_cg_va_copy(p->cg);
+ cfree_cg_push_int(p->cg, 0, p->int_type);
+ return p->int_type;
+ }
+
+ if (toy_sym_is(p, name, "va_arg")) {
+ CfreeCgTypeId ty;
+ if (!toy_parser_expect(p, TOK_LPAREN) || !toy_parse_va_list_addr_arg(p) ||
+ !toy_expect_comma(p)) return CFREE_CG_TYPE_NONE;
+ ty = toy_parse_type(p);
+ if (ty == CFREE_CG_TYPE_NONE) return CFREE_CG_TYPE_NONE;
+ if (!toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE;
+ cfree_cg_va_arg(p->cg, ty);
+ return ty;
+ }
+
if (toy_sym_is(p, name, "fieldtest")) {
uint64_t sz = cfree_cg_type_size(p->c, p->pair_type);
if (!toy_parser_expect(p, TOK_LPAREN) ||
@@ -768,6 +948,58 @@ static CfreeCgTypeId toy_parse_builtin_call(ToyParser* p, CfreeSym name,
return p->int_type;
}
+ if (toy_sym_is(p, name, "asm")) {
+ CfreeSym tmpl;
+ size_t tmpl_len;
+ if (!toy_parser_expect(p, TOK_LPAREN) ||
+ !toy_parse_arch_string(p, &tmpl, &tmpl_len) ||
+ !toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE;
+ if (tmpl_len) {
+ cfree_cg_inline_asm(p->cg, tmpl, NULL, 0, NULL, 0, NULL, 0,
+ CFREE_CG_ASM_VOLATILE);
+ }
+ cfree_cg_push_int(p->cg, 0, p->int_type);
+ return p->int_type;
+ }
+
+ if (toy_sym_is(p, name, "asm_int")) {
+ CfreeSym tmpl;
+ size_t tmpl_len;
+ CfreeCgTypeId a, b;
+ CfreeCgAsmOperand outputs[1];
+ CfreeCgAsmOperand inputs[2];
+ if (!toy_parser_expect(p, TOK_LPAREN) ||
+ !toy_parse_arch_string(p, &tmpl, &tmpl_len) ||
+ !toy_expect_comma(p)) return CFREE_CG_TYPE_NONE;
+ a = toy_parse_expr(p);
+ if (a == CFREE_CG_TYPE_NONE || !toy_expect_comma(p)) return CFREE_CG_TYPE_NONE;
+ b = toy_parse_expr(p);
+ if (b == CFREE_CG_TYPE_NONE) return CFREE_CG_TYPE_NONE;
+ if (!toy_parser_expect(p, TOK_RPAREN)) return CFREE_CG_TYPE_NONE;
+ if (a != p->int_type || b != p->int_type) {
+ toy_error(p, p->cur.loc, "asm_int expects int inputs");
+ return CFREE_CG_TYPE_NONE;
+ }
+ if (tmpl_len) {
+ memset(outputs, 0, sizeof outputs);
+ memset(inputs, 0, sizeof inputs);
+ outputs[0].constraint = cfree_sym_intern(p->c, "=r");
+ outputs[0].type = p->int_type;
+ outputs[0].dir = CFREE_CG_ASM_OUT;
+ inputs[0].constraint = cfree_sym_intern(p->c, "r");
+ inputs[0].type = p->int_type;
+ inputs[0].dir = CFREE_CG_ASM_IN;
+ inputs[1] = inputs[0];
+ cfree_cg_inline_asm(p->cg, tmpl, outputs, 1, inputs, 2, NULL, 0,
+ CFREE_CG_ASM_VOLATILE);
+ } else {
+ cfree_cg_drop(p->cg);
+ cfree_cg_drop(p->cg);
+ cfree_cg_push_int(p->cg, 0, p->int_type);
+ }
+ return p->int_type;
+ }
+
if (toy_sym_is(p, name, "typecheck")) {
CfreeCgTypeId arr, qual, alias, enm, fnty;
CfreeCgEnumValue ev;
@@ -854,12 +1086,19 @@ static CfreeCgTypeId toy_parse_expr_primary(ToyParser* p) {
}
/* Verify argument count */
- if (nargs != fn->nparams) {
+ if ((!fn->variadic && nargs != fn->nparams) ||
+ (fn->variadic && nargs < fn->nparams)) {
toy_error(p, ident_tok.loc,
"function '%s' expects %zu arguments, got %zu",
(const char*)ident_tok.text, fn->nparams, nargs);
return CFREE_CG_TYPE_NONE;
}
+ for (size_t i = 0; i < fn->nparams; ++i) {
+ if (arg_types[i] != fn->params[i]) {
+ toy_error(p, ident_tok.loc, "function argument type mismatch");
+ return CFREE_CG_TYPE_NONE;
+ }
+ }
cfree_cg_call(p->cg, (uint32_t)nargs, fn->type);
return fn->ret;
@@ -1380,6 +1619,10 @@ static int toy_parse_return_stmt(ToyParser* p) {
toy_error(p, p->cur.loc, "undefined function in tail call");
return 0;
}
+ if (fn->variadic) {
+ toy_error(p, p->cur.loc, "tail call to variadic function unsupported");
+ return 0;
+ }
toy_parser_advance(p);
if (!toy_parser_expect(p, TOK_LPAREN)) return 0;
cfree_cg_push_symbol(p->cg, name, fn->type, CFREE_CG_SYMREF_ADDR, 0);
@@ -1535,6 +1778,7 @@ static int toy_parse_fn(ToyParser* p) {
CfreeCgTypeId param_types[TOY_MAX_PARAMS];
CfreeSym param_names[TOY_MAX_PARAMS];
size_t nparams = 0;
+ int variadic = 0;
CfreeCgDeclAttrs attrs;
CfreeCgTypeId fn_ty;
ToyFn* fn_entry;
@@ -1556,6 +1800,11 @@ static int toy_parse_fn(ToyParser* p) {
if (p->cur.kind != TOK_RPAREN) {
for (;;) {
+ if (p->cur.kind == TOK_DOTDOTDOT) {
+ variadic = 1;
+ toy_parser_advance(p);
+ break;
+ }
if (p->cur.kind != TOK_IDENT) {
toy_error(p, p->cur.loc, "expected parameter name");
return -1;
@@ -1575,6 +1824,11 @@ static int toy_parse_fn(ToyParser* p) {
nparams++;
if (p->cur.kind == TOK_COMMA) {
toy_parser_advance(p);
+ if (p->cur.kind == TOK_DOTDOTDOT) {
+ variadic = 1;
+ toy_parser_advance(p);
+ break;
+ }
} else {
break;
}
@@ -1593,7 +1847,8 @@ static int toy_parse_fn(ToyParser* p) {
}
/* Build function type */
- fn_ty = cfree_cg_type_func(p->c, ret_type, param_types, (uint32_t)nparams, 0);
+ fn_ty = cfree_cg_type_func(p->c, ret_type, param_types, (uint32_t)nparams,
+ variadic);
if (fn_ty == CFREE_CG_TYPE_NONE) {
toy_error(p, p->cur.loc, "failed to create function type");
return -1;
@@ -1609,6 +1864,7 @@ static int toy_parse_fn(ToyParser* p) {
fn_entry->type = fn_ty;
fn_entry->ret = ret_type;
fn_entry->nparams = nparams;
+ fn_entry->variadic = variadic;
for (i = 0; i < nparams; i++) fn_entry->params[i] = param_types[i];
p->nfns++;
diff --git a/src/api/lifecycle.c b/src/api/lifecycle.c
@@ -3,6 +3,7 @@
* don't drag in the full compile/link pipeline through the linker. */
#include <cfree.h>
+#include <string.h>
#include "core/core.h"
#include "core/heap.h"
@@ -28,6 +29,13 @@ void cfree_compiler_free(CfreeCompiler* c) {
h->free(h, c, sizeof(*c));
}
+CfreeTarget cfree_compiler_target(CfreeCompiler* c) {
+ CfreeTarget t;
+ memset(&t, 0, sizeof t);
+ if (!c) return t;
+ return c->target;
+}
+
const char* cfree_compiler_file_name(CfreeCompiler* c, uint32_t file_id) {
const SourceFile* f;
if (!c) return NULL;
diff --git a/test/toy/cases/18_cg_api_field_tail.toy b/test/toy/cases/18_cg_api_field_tail.toy
@@ -7,5 +7,5 @@ fn id(x: int): int {
}
fn main(): int {
- return fieldtest() + id(0) + asmnop();
+ return fieldtest() + id(0) + asm(arch("nop", "", ""));
}
diff --git a/test/toy/cases/19_cg_api_variadic_asm.expected b/test/toy/cases/19_cg_api_variadic_asm.expected
@@ -0,0 +1 @@
+65
diff --git a/test/toy/cases/19_cg_api_variadic_asm.toy b/test/toy/cases/19_cg_api_variadic_asm.toy
@@ -0,0 +1,31 @@
+fn sum_first(n: int, ...): int {
+ let ap: va_list;
+ va_start(ap);
+
+ let cp: va_list;
+ va_copy(cp, ap);
+ let first: int = va_arg(cp, int);
+ va_end(cp);
+
+ let i: int = 0;
+ let s: int = 0;
+ while i < n {
+ s = s + va_arg(ap, int);
+ i = i + 1;
+ }
+ va_end(ap);
+ return s + first;
+}
+
+fn main(): int {
+ let v: int = asm_int(arch("add %x0, %x1, %x2", "", ""), 19, 23);
+ let s: int = 23;
+ asm(arch("nop", "", ""));
+ if target() != 1 {
+ v = 42;
+ }
+ if target_os() != 2 {
+ s = sum_first(3, 5, 6, 7);
+ }
+ return s + v;
+}
diff --git a/test/toy/demo.toy b/test/toy/demo.toy
@@ -58,7 +58,7 @@ fn intrin_demo(x: int): int {
}
fn api_demo(): int {
- return typecheck() + byteconst() + fieldtest() + asmnop();
+ return typecheck() + byteconst() + fieldtest() + asm(arch("nop", "", ""));
}
fn main(): int {