commit 26a7338891273c9c3c5d5b7b3a0710b002bc9bd3
parent 58cae09b4b02e5c3df711540a0572f21f7da59db
Author: Ryan Sepassi <rsepassi@gmail.com>
Date: Wed, 3 Jun 2026 10:30:28 -0700
toy: table-drive dot-const parsers, flag loops, mem-xfer scaffolding
Fold nine .name->enum if/else-if parsers onto a shared ToyConstRow table
plus toy_lookup_const/toy_parse_dot_const helpers, reuse the same table for
the three flag-mask loops via toy_parse_flag_set, and merge the byte-identical
memcpy/memmove builtins into toy_parse_mem_copy_move(is_move). Diagnostics,
IDENT-vs-keyword strictness, and the memset specifics are preserved.
(cherry picked from commit 5ecac80632017e970727ab0d01468e098470357b)
Diffstat:
6 files changed, 270 insertions(+), 356 deletions(-)
diff --git a/lang/toy/asm.c b/lang/toy/asm.c
@@ -279,33 +279,22 @@ static int toy_asm_record_field_by_name(ToyParser* p, KitCgTypeId record_ty,
}
static int toy_parse_asm_flags(ToyParser* p, uint32_t* flags) {
+ static const ToyConstRow rows[] = {
+ {"volatile", KIT_CG_ASM_VOLATILE},
+ {"pure", KIT_CG_ASM_PURE},
+ {"nomem", KIT_CG_ASM_NOMEM},
+ {"readonly", KIT_CG_ASM_READONLY},
+ {"preserves_flags", KIT_CG_ASM_PRESERVES_FLAGS},
+ {"nostack", KIT_CG_ASM_NOSTACK},
+ {"noreturn", KIT_CG_ASM_NORETURN},
+ };
if (!toy_expect_ident(p, "flags") || !toy_parser_expect(p, TOK_LPAREN)) {
toy_error(p, p->cur.loc, "expected flags(...)");
return 0;
}
- while (p->cur.kind != TOK_RPAREN && p->cur.kind != TOK_EOF) {
- KitSym name;
- if (!toy_parse_attr_dot_name(p, &name)) return 0;
- if (toy_sym_is(p, name, "volatile"))
- *flags |= KIT_CG_ASM_VOLATILE;
- else if (toy_sym_is(p, name, "pure"))
- *flags |= KIT_CG_ASM_PURE;
- else if (toy_sym_is(p, name, "nomem"))
- *flags |= KIT_CG_ASM_NOMEM;
- else if (toy_sym_is(p, name, "readonly"))
- *flags |= KIT_CG_ASM_READONLY;
- else if (toy_sym_is(p, name, "preserves_flags"))
- *flags |= KIT_CG_ASM_PRESERVES_FLAGS;
- else if (toy_sym_is(p, name, "nostack"))
- *flags |= KIT_CG_ASM_NOSTACK;
- else if (toy_sym_is(p, name, "noreturn"))
- *flags |= KIT_CG_ASM_NORETURN;
- else {
- toy_error(p, p->cur.loc, "unknown asm flag");
- return 0;
- }
- if (!toy_parser_match(p, TOK_COMMA)) break;
- }
+ if (!toy_parse_flag_set(p, rows, sizeof rows / sizeof rows[0], "asm flag", 0,
+ flags))
+ return 0;
if (!toy_parser_expect(p, TOK_RPAREN)) {
toy_error(p, p->cur.loc, "expected ')' after asm flags");
return 0;
@@ -314,24 +303,18 @@ static int toy_parse_asm_flags(ToyParser* p, uint32_t* flags) {
}
static int toy_parse_asm_clobber_abi(ToyParser* p, uint32_t* clobber_abi_sets) {
+ static const ToyConstRow rows[] = {
+ {"caller_saved", KIT_CG_ASM_CLOBBER_ABI_CALLER_SAVED},
+ {"callee_saved", KIT_CG_ASM_CLOBBER_ABI_CALLEE_SAVED},
+ };
if (!toy_expect_ident(p, "clobber_abi") ||
!toy_parser_expect(p, TOK_LPAREN)) {
toy_error(p, p->cur.loc, "expected clobber_abi(...)");
return 0;
}
- while (p->cur.kind != TOK_RPAREN && p->cur.kind != TOK_EOF) {
- KitSym name;
- if (!toy_parse_attr_dot_name(p, &name)) return 0;
- if (toy_sym_is(p, name, "caller_saved"))
- *clobber_abi_sets |= KIT_CG_ASM_CLOBBER_ABI_CALLER_SAVED;
- else if (toy_sym_is(p, name, "callee_saved"))
- *clobber_abi_sets |= KIT_CG_ASM_CLOBBER_ABI_CALLEE_SAVED;
- else {
- toy_error(p, p->cur.loc, "unknown asm clobber ABI");
- return 0;
- }
- if (!toy_parser_match(p, TOK_COMMA)) break;
- }
+ if (!toy_parse_flag_set(p, rows, sizeof rows / sizeof rows[0],
+ "asm clobber ABI", 0, clobber_abi_sets))
+ return 0;
if (!toy_parser_expect(p, TOK_RPAREN)) {
toy_error(p, p->cur.loc, "expected ')' after asm clobber ABI");
return 0;
diff --git a/lang/toy/attrs.c b/lang/toy/attrs.c
@@ -51,29 +51,16 @@ static int toy_parse_attr_string_arg(ToyParser* p, KitSym* out) {
}
int toy_parse_callconv_const(ToyParser* p, KitCgCallConv* out) {
- KitSym name;
- if (!toy_parser_expect(p, TOK_DOT) || p->cur.kind != TOK_IDENT) {
- toy_error(p, p->cur.loc, "expected call convention");
+ static const ToyConstRow rows[] = {
+ {"target_c", KIT_CG_CC_TARGET_C}, {"sysv", KIT_CG_CC_SYSV},
+ {"win64", KIT_CG_CC_WIN64}, {"aapcs", KIT_CG_CC_AAPCS},
+ {"wasm", KIT_CG_CC_WASM}, {"interrupt", KIT_CG_CC_INTERRUPT},
+ };
+ uint64_t v;
+ if (!toy_parse_dot_const(p, rows, sizeof rows / sizeof rows[0], 1,
+ "call convention", "call convention", &v))
return 0;
- }
- name = toy_tok_sym(p, p->cur);
- toy_parser_advance(p);
- if (toy_sym_is(p, name, "target_c"))
- *out = KIT_CG_CC_TARGET_C;
- else if (toy_sym_is(p, name, "sysv"))
- *out = KIT_CG_CC_SYSV;
- else if (toy_sym_is(p, name, "win64"))
- *out = KIT_CG_CC_WIN64;
- else if (toy_sym_is(p, name, "aapcs"))
- *out = KIT_CG_CC_AAPCS;
- else if (toy_sym_is(p, name, "wasm"))
- *out = KIT_CG_CC_WASM;
- else if (toy_sym_is(p, name, "interrupt"))
- *out = KIT_CG_CC_INTERRUPT;
- else {
- toy_error(p, p->cur.loc, "unknown call convention");
- return 0;
- }
+ *out = (KitCgCallConv)v;
return 1;
}
diff --git a/lang/toy/builtins.c b/lang/toy/builtins.c
@@ -10,17 +10,9 @@ static KitCgMemAccess toy_mem_access_align(ToyParser* p, KitCgTypeId type,
}
static int toy_parse_mem_flags_tail(ToyParser* p, KitCgMemAccess* access) {
- while (toy_parser_match(p, TOK_COMMA)) {
- KitSym flag;
- if (!toy_parse_attr_dot_name(p, &flag)) return 0;
- if (toy_sym_is(p, flag, "volatile"))
- access->flags |= KIT_CG_MEM_VOLATILE;
- else {
- toy_error(p, p->cur.loc, "unknown memory access flag");
- return 0;
- }
- }
- return 1;
+ static const ToyConstRow rows[] = {{"volatile", KIT_CG_MEM_VOLATILE}};
+ return toy_parse_flag_set(p, rows, sizeof rows / sizeof rows[0],
+ "memory access flag", 1, &access->flags);
}
static void toy_store_top_to_local(ToyParser* p, KitCgLocal local,
@@ -151,72 +143,45 @@ static int toy_parse_optional_access_arg(ToyParser* p, KitCgMemAccess* access) {
}
static int toy_parse_mem_order(ToyParser* p, KitCgMemOrder* out) {
- KitSym name;
- if (!toy_parser_expect(p, TOK_DOT) || p->cur.kind != TOK_IDENT) {
- toy_error(p, p->cur.loc, "expected memory order");
- return 0;
- }
- name = toy_tok_sym(p, p->cur);
- toy_parser_advance(p);
- if (toy_sym_is(p, name, "relaxed"))
- *out = KIT_CG_MO_RELAXED;
- else if (toy_sym_is(p, name, "consume"))
- *out = KIT_CG_MO_CONSUME;
- else if (toy_sym_is(p, name, "acquire"))
- *out = KIT_CG_MO_ACQUIRE;
- else if (toy_sym_is(p, name, "release"))
- *out = KIT_CG_MO_RELEASE;
- else if (toy_sym_is(p, name, "acq_rel"))
- *out = KIT_CG_MO_ACQ_REL;
- else if (toy_sym_is(p, name, "seq_cst"))
- *out = KIT_CG_MO_SEQ_CST;
- else {
- toy_error(p, p->cur.loc, "unknown memory order");
+ static const ToyConstRow rows[] = {
+ {"relaxed", KIT_CG_MO_RELAXED}, {"consume", KIT_CG_MO_CONSUME},
+ {"acquire", KIT_CG_MO_ACQUIRE}, {"release", KIT_CG_MO_RELEASE},
+ {"acq_rel", KIT_CG_MO_ACQ_REL}, {"seq_cst", KIT_CG_MO_SEQ_CST},
+ };
+ uint64_t v;
+ if (!toy_parse_dot_const(p, rows, sizeof rows / sizeof rows[0], 1,
+ "memory order", "memory order", &v))
return 0;
- }
+ *out = (KitCgMemOrder)v;
return 1;
}
static int toy_parse_atomic_op(ToyParser* p, KitCgAtomicOp* out) {
- KitSym name;
- if (!toy_parse_attr_dot_name(p, &name)) return 0;
- if (toy_sym_is(p, name, "xchg"))
- *out = KIT_CG_ATOMIC_XCHG;
- else if (toy_sym_is(p, name, "add"))
- *out = KIT_CG_ATOMIC_ADD;
- else if (toy_sym_is(p, name, "sub"))
- *out = KIT_CG_ATOMIC_SUB;
- else if (toy_sym_is(p, name, "and"))
- *out = KIT_CG_ATOMIC_AND;
- else if (toy_sym_is(p, name, "or"))
- *out = KIT_CG_ATOMIC_OR;
- else if (toy_sym_is(p, name, "xor"))
- *out = KIT_CG_ATOMIC_XOR;
- else if (toy_sym_is(p, name, "nand"))
- *out = KIT_CG_ATOMIC_NAND;
- else {
- toy_error(p, p->cur.loc, "unknown atomic op");
+ static const ToyConstRow rows[] = {
+ {"xchg", KIT_CG_ATOMIC_XCHG}, {"add", KIT_CG_ATOMIC_ADD},
+ {"sub", KIT_CG_ATOMIC_SUB}, {"and", KIT_CG_ATOMIC_AND},
+ {"or", KIT_CG_ATOMIC_OR}, {"xor", KIT_CG_ATOMIC_XOR},
+ {"nand", KIT_CG_ATOMIC_NAND},
+ };
+ uint64_t v;
+ if (!toy_parse_dot_const(p, rows, sizeof rows / sizeof rows[0], 0, NULL,
+ "atomic op", &v))
return 0;
- }
+ *out = (KitCgAtomicOp)v;
return 1;
}
static int toy_parse_cmpxchg_strength(ToyParser* p, int* weak_out) {
- KitSym name;
- if (!toy_parser_expect(p, TOK_DOT) || p->cur.kind != TOK_IDENT) {
- toy_error(p, p->cur.loc, "expected compare-exchange strength");
+ static const ToyConstRow rows[] = {
+ {"strong", 0},
+ {"weak", 1},
+ };
+ uint64_t v;
+ if (!toy_parse_dot_const(p, rows, sizeof rows / sizeof rows[0], 1,
+ "compare-exchange strength",
+ "compare-exchange strength", &v))
return 0;
- }
- name = toy_tok_sym(p, p->cur);
- toy_parser_advance(p);
- if (toy_sym_is(p, name, "strong"))
- *weak_out = 0;
- else if (toy_sym_is(p, name, "weak"))
- *weak_out = 1;
- else {
- toy_error(p, p->cur.loc, "unknown compare-exchange strength");
- return 0;
- }
+ *weak_out = (int)v;
return 1;
}
@@ -394,24 +359,19 @@ static KitCgTypeId toy_parse_call_builtin(ToyParser* p) {
}
static int toy_parse_barrier_scope(ToyParser* p, KitCgBarrierScope* out) {
- KitSym name;
- if (!toy_parse_attr_dot_name(p, &name)) return 0;
- if (toy_sym_is(p, name, "full"))
- *out = KIT_CG_BARRIER_FULL;
- else if (toy_sym_is(p, name, "inner"))
- *out = KIT_CG_BARRIER_INNER;
- else if (toy_sym_is(p, name, "inner_store"))
- *out = KIT_CG_BARRIER_INNER_STORE;
- else if (toy_sym_is(p, name, "outer"))
- *out = KIT_CG_BARRIER_OUTER;
- else if (toy_sym_is(p, name, "outer_store"))
- *out = KIT_CG_BARRIER_OUTER_STORE;
- else if (toy_sym_is(p, name, "non_share"))
- *out = KIT_CG_BARRIER_NON_SHARE;
- else {
- toy_error(p, p->cur.loc, "unknown barrier scope");
+ static const ToyConstRow rows[] = {
+ {"full", KIT_CG_BARRIER_FULL},
+ {"inner", KIT_CG_BARRIER_INNER},
+ {"inner_store", KIT_CG_BARRIER_INNER_STORE},
+ {"outer", KIT_CG_BARRIER_OUTER},
+ {"outer_store", KIT_CG_BARRIER_OUTER_STORE},
+ {"non_share", KIT_CG_BARRIER_NON_SHARE},
+ };
+ uint64_t v;
+ if (!toy_parse_dot_const(p, rows, sizeof rows / sizeof rows[0], 0, NULL,
+ "barrier scope", &v))
return 0;
- }
+ *out = (KitCgBarrierScope)v;
return 1;
}
@@ -1079,6 +1039,63 @@ KitCgTypeId toy_parse_generic_builtin(ToyParser* p, KitSym name,
return KIT_CG_TYPE_NONE;
}
+/* memcpy and memmove are identical except for the constant-size emission:
+ * memcpy lowers to kit_cg_memcpy, memmove to kit_cg_memmove. The dynamic path
+ * is byte-for-byte shared (both emit a forward element loop). */
+static KitCgTypeId toy_parse_mem_copy_move(ToyParser* p, int is_move) {
+ KitCgTypeId dst, src;
+ int64_t size = 0, align = 0;
+ int dynamic_size = 0, dynamic_align = 0;
+ KitCgLocal dst_local = KIT_CG_LOCAL_NONE;
+ KitCgLocal src_local = KIT_CG_LOCAL_NONE;
+ KitCgLocal size_local = KIT_CG_LOCAL_NONE;
+ KitCgMemAccess access;
+ KitCgTypeId u8_ty = toy_builtin_type(p, KIT_CG_BUILTIN_I8);
+ KitCgTypeId u8_ptr_ty = kit_cg_type_ptr(p->c, u8_ty, 0);
+ toy_parser_advance(p);
+ dst = toy_parse_expr(p);
+ if (dst == KIT_CG_TYPE_NONE || !toy_expect_comma(p)) return KIT_CG_TYPE_NONE;
+ src = toy_parse_expr(p);
+ if (src == KIT_CG_TYPE_NONE || !toy_expect_comma(p)) return KIT_CG_TYPE_NONE;
+ if (p->cur.kind == TOK_NUMBER && !p->cur.is_float) {
+ if (!toy_parse_number_arg(p, &size)) return KIT_CG_TYPE_NONE;
+ } else {
+ KitCgTypeId size_ty = toy_parse_expr(p);
+ if (size_ty == KIT_CG_TYPE_NONE) return KIT_CG_TYPE_NONE;
+ if (size_ty != p->int_type) {
+ toy_error(p, p->cur.loc, "memory size must be i64");
+ return KIT_CG_TYPE_NONE;
+ }
+ dynamic_size = 1;
+ size_local = kit_cg_local(p->cg, p->int_type, toy_slot_attrs(0));
+ toy_store_top_to_local(p, size_local, p->int_type);
+ }
+ if (!toy_parse_memory_align_operand(p, &align, &dynamic_align))
+ return KIT_CG_TYPE_NONE;
+ access = toy_mem_access_align(p, p->int_type, (uint32_t)align);
+ if (!toy_parse_mem_flags_tail(p, &access)) return KIT_CG_TYPE_NONE;
+ if (!toy_parser_expect(p, TOK_RPAREN)) return KIT_CG_TYPE_NONE;
+ if (dynamic_size || dynamic_align) {
+ if (!dynamic_size) {
+ size_local = kit_cg_local(p->cg, p->int_type, toy_slot_attrs(0));
+ toy_store_const_to_local(p, size_local, p->int_type, (uint64_t)size);
+ }
+ src_local = kit_cg_local(p->cg, u8_ptr_ty, toy_slot_attrs(0));
+ kit_cg_bitcast(p->cg, u8_ptr_ty);
+ toy_store_top_to_local(p, src_local, u8_ptr_ty);
+ dst_local = kit_cg_local(p->cg, u8_ptr_ty, toy_slot_attrs(0));
+ kit_cg_bitcast(p->cg, u8_ptr_ty);
+ toy_store_top_to_local(p, dst_local, u8_ptr_ty);
+ toy_emit_dynamic_memory_loop(p, dst_local, src_local, size_local, 0, 0);
+ return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
+ }
+ if (is_move)
+ kit_cg_memmove(p->cg, (uint64_t)size, access, access);
+ else
+ kit_cg_memcpy(p->cg, (uint64_t)size, access, access);
+ return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
+}
+
KitCgTypeId toy_parse_memory_builtin_call(ToyParser* p, KitSym name,
int* recognized) {
*recognized = 1;
@@ -1135,111 +1152,9 @@ KitCgTypeId toy_parse_memory_builtin_call(ToyParser* p, KitSym name,
return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
}
- if (toy_sym_is(p, name, "memcpy")) {
- KitCgTypeId dst, src;
- int64_t size = 0, align = 0;
- int dynamic_size = 0, dynamic_align = 0;
- KitCgLocal dst_local = KIT_CG_LOCAL_NONE;
- KitCgLocal src_local = KIT_CG_LOCAL_NONE;
- KitCgLocal size_local = KIT_CG_LOCAL_NONE;
- KitCgMemAccess access;
- KitCgTypeId u8_ty = toy_builtin_type(p, KIT_CG_BUILTIN_I8);
- KitCgTypeId u8_ptr_ty = kit_cg_type_ptr(p->c, u8_ty, 0);
- toy_parser_advance(p);
- dst = toy_parse_expr(p);
- if (dst == KIT_CG_TYPE_NONE || !toy_expect_comma(p))
- return KIT_CG_TYPE_NONE;
- src = toy_parse_expr(p);
- if (src == KIT_CG_TYPE_NONE || !toy_expect_comma(p))
- return KIT_CG_TYPE_NONE;
- if (p->cur.kind == TOK_NUMBER && !p->cur.is_float) {
- if (!toy_parse_number_arg(p, &size)) return KIT_CG_TYPE_NONE;
- } else {
- KitCgTypeId size_ty = toy_parse_expr(p);
- if (size_ty == KIT_CG_TYPE_NONE) return KIT_CG_TYPE_NONE;
- if (size_ty != p->int_type) {
- toy_error(p, p->cur.loc, "memory size must be i64");
- return KIT_CG_TYPE_NONE;
- }
- dynamic_size = 1;
- size_local = kit_cg_local(p->cg, p->int_type, toy_slot_attrs(0));
- toy_store_top_to_local(p, size_local, p->int_type);
- }
- if (!toy_parse_memory_align_operand(p, &align, &dynamic_align))
- return KIT_CG_TYPE_NONE;
- access = toy_mem_access_align(p, p->int_type, (uint32_t)align);
- if (!toy_parse_mem_flags_tail(p, &access)) return KIT_CG_TYPE_NONE;
- if (!toy_parser_expect(p, TOK_RPAREN)) return KIT_CG_TYPE_NONE;
- if (dynamic_size || dynamic_align) {
- if (!dynamic_size) {
- size_local = kit_cg_local(p->cg, p->int_type, toy_slot_attrs(0));
- toy_store_const_to_local(p, size_local, p->int_type, (uint64_t)size);
- }
- src_local = kit_cg_local(p->cg, u8_ptr_ty, toy_slot_attrs(0));
- kit_cg_bitcast(p->cg, u8_ptr_ty);
- toy_store_top_to_local(p, src_local, u8_ptr_ty);
- dst_local = kit_cg_local(p->cg, u8_ptr_ty, toy_slot_attrs(0));
- kit_cg_bitcast(p->cg, u8_ptr_ty);
- toy_store_top_to_local(p, dst_local, u8_ptr_ty);
- toy_emit_dynamic_memory_loop(p, dst_local, src_local, size_local, 0, 0);
- return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
- }
- kit_cg_memcpy(p->cg, (uint64_t)size, access, access);
- return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
- }
+ if (toy_sym_is(p, name, "memcpy")) return toy_parse_mem_copy_move(p, 0);
- if (toy_sym_is(p, name, "memmove")) {
- KitCgTypeId dst, src;
- int64_t size = 0, align = 0;
- int dynamic_size = 0, dynamic_align = 0;
- KitCgLocal dst_local = KIT_CG_LOCAL_NONE;
- KitCgLocal src_local = KIT_CG_LOCAL_NONE;
- KitCgLocal size_local = KIT_CG_LOCAL_NONE;
- KitCgMemAccess access;
- KitCgTypeId u8_ty = toy_builtin_type(p, KIT_CG_BUILTIN_I8);
- KitCgTypeId u8_ptr_ty = kit_cg_type_ptr(p->c, u8_ty, 0);
- toy_parser_advance(p);
- dst = toy_parse_expr(p);
- if (dst == KIT_CG_TYPE_NONE || !toy_expect_comma(p))
- return KIT_CG_TYPE_NONE;
- src = toy_parse_expr(p);
- if (src == KIT_CG_TYPE_NONE || !toy_expect_comma(p))
- return KIT_CG_TYPE_NONE;
- if (p->cur.kind == TOK_NUMBER && !p->cur.is_float) {
- if (!toy_parse_number_arg(p, &size)) return KIT_CG_TYPE_NONE;
- } else {
- KitCgTypeId size_ty = toy_parse_expr(p);
- if (size_ty == KIT_CG_TYPE_NONE) return KIT_CG_TYPE_NONE;
- if (size_ty != p->int_type) {
- toy_error(p, p->cur.loc, "memory size must be i64");
- return KIT_CG_TYPE_NONE;
- }
- dynamic_size = 1;
- size_local = kit_cg_local(p->cg, p->int_type, toy_slot_attrs(0));
- toy_store_top_to_local(p, size_local, p->int_type);
- }
- if (!toy_parse_memory_align_operand(p, &align, &dynamic_align))
- return KIT_CG_TYPE_NONE;
- access = toy_mem_access_align(p, p->int_type, (uint32_t)align);
- if (!toy_parse_mem_flags_tail(p, &access)) return KIT_CG_TYPE_NONE;
- if (!toy_parser_expect(p, TOK_RPAREN)) return KIT_CG_TYPE_NONE;
- if (dynamic_size || dynamic_align) {
- if (!dynamic_size) {
- size_local = kit_cg_local(p->cg, p->int_type, toy_slot_attrs(0));
- toy_store_const_to_local(p, size_local, p->int_type, (uint64_t)size);
- }
- src_local = kit_cg_local(p->cg, u8_ptr_ty, toy_slot_attrs(0));
- kit_cg_bitcast(p->cg, u8_ptr_ty);
- toy_store_top_to_local(p, src_local, u8_ptr_ty);
- dst_local = kit_cg_local(p->cg, u8_ptr_ty, toy_slot_attrs(0));
- kit_cg_bitcast(p->cg, u8_ptr_ty);
- toy_store_top_to_local(p, dst_local, u8_ptr_ty);
- toy_emit_dynamic_memory_loop(p, dst_local, src_local, size_local, 0, 0);
- return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
- }
- kit_cg_memmove(p->cg, (uint64_t)size, access, access);
- return toy_builtin_type(p, KIT_CG_BUILTIN_VOID);
- }
+ if (toy_sym_is(p, name, "memmove")) return toy_parse_mem_copy_move(p, 1);
if (toy_sym_is(p, name, "atomic_fence")) {
KitCgMemOrder order;
diff --git a/lang/toy/expr.c b/lang/toy/expr.c
@@ -348,131 +348,79 @@ int toy_parse_switch_label_value(ToyParser* p, KitCgTypeId selector_ty,
}
int toy_parse_symbol_feature_const(ToyParser* p, KitCgSymbolFeature* out) {
- KitSym name;
- if (!toy_parser_expect(p, TOK_DOT) || p->cur.kind != TOK_IDENT) {
- toy_error(p, p->cur.loc, "expected symbol feature");
- return 0;
- }
- name = toy_tok_sym(p, p->cur);
- toy_parser_advance(p);
- if (toy_sym_is(p, name, "weak"))
- *out = KIT_CG_SYMFEAT_WEAK;
- else if (toy_sym_is(p, name, "protected_visibility"))
- *out = KIT_CG_SYMFEAT_PROTECTED_VISIBILITY;
- else if (toy_sym_is(p, name, "dllimport"))
- *out = KIT_CG_SYMFEAT_DLLIMPORT;
- else if (toy_sym_is(p, name, "dllexport"))
- *out = KIT_CG_SYMFEAT_DLLEXPORT;
- else if (toy_sym_is(p, name, "comdat"))
- *out = KIT_CG_SYMFEAT_COMDAT;
- else if (toy_sym_is(p, name, "common"))
- *out = KIT_CG_SYMFEAT_COMMON;
- else if (toy_sym_is(p, name, "merge_sections"))
- *out = KIT_CG_SYMFEAT_MERGE_SECTIONS;
- else if (toy_sym_is(p, name, "constructor_priority"))
- *out = KIT_CG_SYMFEAT_CONSTRUCTOR_PRIORITY;
- else if (toy_sym_is(p, name, "tls_local_exec"))
- *out = KIT_CG_SYMFEAT_TLS_LOCAL_EXEC;
- else if (toy_sym_is(p, name, "tls_initial_exec"))
- *out = KIT_CG_SYMFEAT_TLS_INITIAL_EXEC;
- else if (toy_sym_is(p, name, "tls_local_dynamic"))
- *out = KIT_CG_SYMFEAT_TLS_LOCAL_DYNAMIC;
- else if (toy_sym_is(p, name, "tls_general_dynamic"))
- *out = KIT_CG_SYMFEAT_TLS_GENERAL_DYNAMIC;
- else {
- toy_error(p, p->cur.loc, "unknown symbol feature");
+ static const ToyConstRow rows[] = {
+ {"weak", KIT_CG_SYMFEAT_WEAK},
+ {"protected_visibility", KIT_CG_SYMFEAT_PROTECTED_VISIBILITY},
+ {"dllimport", KIT_CG_SYMFEAT_DLLIMPORT},
+ {"dllexport", KIT_CG_SYMFEAT_DLLEXPORT},
+ {"comdat", KIT_CG_SYMFEAT_COMDAT},
+ {"common", KIT_CG_SYMFEAT_COMMON},
+ {"merge_sections", KIT_CG_SYMFEAT_MERGE_SECTIONS},
+ {"constructor_priority", KIT_CG_SYMFEAT_CONSTRUCTOR_PRIORITY},
+ {"tls_local_exec", KIT_CG_SYMFEAT_TLS_LOCAL_EXEC},
+ {"tls_initial_exec", KIT_CG_SYMFEAT_TLS_INITIAL_EXEC},
+ {"tls_local_dynamic", KIT_CG_SYMFEAT_TLS_LOCAL_DYNAMIC},
+ {"tls_general_dynamic", KIT_CG_SYMFEAT_TLS_GENERAL_DYNAMIC},
+ };
+ uint64_t v;
+ if (!toy_parse_dot_const(p, rows, sizeof rows / sizeof rows[0], 1,
+ "symbol feature", "symbol feature", &v))
return 0;
- }
+ *out = (KitCgSymbolFeature)v;
return 1;
}
int toy_parse_backend_feature_const(ToyParser* p, uint64_t* out) {
- KitSym name;
- if (!toy_parser_expect(p, TOK_DOT) || p->cur.kind != TOK_IDENT) {
- toy_error(p, p->cur.loc, "expected backend feature");
- return 0;
- }
- name = toy_tok_sym(p, p->cur);
- toy_parser_advance(p);
- if (toy_sym_is(p, name, "unaligned_memory"))
- *out = KIT_CG_BACKEND_UNALIGNED_MEMORY;
- else if (toy_sym_is(p, name, "strict_alignment"))
- *out = KIT_CG_BACKEND_STRICT_ALIGNMENT;
- else if (toy_sym_is(p, name, "red_zone"))
- *out = KIT_CG_BACKEND_RED_ZONE;
- else if (toy_sym_is(p, name, "simd"))
- *out = KIT_CG_BACKEND_SIMD;
- else if (toy_sym_is(p, name, "pointer_auth"))
- *out = KIT_CG_BACKEND_POINTER_AUTH;
- else if (toy_sym_is(p, name, "branch_protection"))
- *out = KIT_CG_BACKEND_BRANCH_PROTECTION;
- else {
- toy_error(p, p->cur.loc, "unknown backend feature");
- return 0;
- }
- return 1;
+ static const ToyConstRow rows[] = {
+ {"unaligned_memory", KIT_CG_BACKEND_UNALIGNED_MEMORY},
+ {"strict_alignment", KIT_CG_BACKEND_STRICT_ALIGNMENT},
+ {"red_zone", KIT_CG_BACKEND_RED_ZONE},
+ {"simd", KIT_CG_BACKEND_SIMD},
+ {"pointer_auth", KIT_CG_BACKEND_POINTER_AUTH},
+ {"branch_protection", KIT_CG_BACKEND_BRANCH_PROTECTION},
+ };
+ return toy_parse_dot_const(p, rows, sizeof rows / sizeof rows[0], 1,
+ "backend feature", "backend feature", out);
}
int toy_parse_intrinsic_const(ToyParser* p, KitCgIntrinsic* out) {
- KitSym name;
- if (!toy_parser_expect(p, TOK_DOT) || p->cur.kind != TOK_IDENT) {
- toy_error(p, p->cur.loc, "expected intrinsic name");
- return 0;
- }
- name = toy_tok_sym(p, p->cur);
- toy_parser_advance(p);
- if (toy_sym_is(p, name, "cpu_nop"))
- *out = KIT_CG_INTRIN_CPU_NOP;
- else if (toy_sym_is(p, name, "cpu_yield"))
- *out = KIT_CG_INTRIN_CPU_YIELD;
- else if (toy_sym_is(p, name, "wfi"))
- *out = KIT_CG_INTRIN_WFI;
- else if (toy_sym_is(p, name, "wfe"))
- *out = KIT_CG_INTRIN_WFE;
- else if (toy_sym_is(p, name, "sev"))
- *out = KIT_CG_INTRIN_SEV;
- else if (toy_sym_is(p, name, "isb"))
- *out = KIT_CG_INTRIN_ISB;
- else if (toy_sym_is(p, name, "dmb"))
- *out = KIT_CG_INTRIN_DMB;
- else if (toy_sym_is(p, name, "dsb"))
- *out = KIT_CG_INTRIN_DSB;
- else if (toy_sym_is(p, name, "irq_save"))
- *out = KIT_CG_INTRIN_IRQ_SAVE;
- else if (toy_sym_is(p, name, "irq_restore"))
- *out = KIT_CG_INTRIN_IRQ_RESTORE;
- else if (toy_sym_is(p, name, "irq_enable"))
- *out = KIT_CG_INTRIN_IRQ_ENABLE;
- else if (toy_sym_is(p, name, "irq_disable"))
- *out = KIT_CG_INTRIN_IRQ_DISABLE;
- else if (toy_sym_is(p, name, "syscall"))
- *out = KIT_CG_INTRIN_SYSCALL;
- else if (toy_sym_is(p, name, "coro_switch"))
- *out = KIT_CG_INTRIN_CORO_SWITCH;
- else {
- toy_error(p, p->cur.loc, "unknown intrinsic");
+ static const ToyConstRow rows[] = {
+ {"cpu_nop", KIT_CG_INTRIN_CPU_NOP},
+ {"cpu_yield", KIT_CG_INTRIN_CPU_YIELD},
+ {"wfi", KIT_CG_INTRIN_WFI},
+ {"wfe", KIT_CG_INTRIN_WFE},
+ {"sev", KIT_CG_INTRIN_SEV},
+ {"isb", KIT_CG_INTRIN_ISB},
+ {"dmb", KIT_CG_INTRIN_DMB},
+ {"dsb", KIT_CG_INTRIN_DSB},
+ {"irq_save", KIT_CG_INTRIN_IRQ_SAVE},
+ {"irq_restore", KIT_CG_INTRIN_IRQ_RESTORE},
+ {"irq_enable", KIT_CG_INTRIN_IRQ_ENABLE},
+ {"irq_disable", KIT_CG_INTRIN_IRQ_DISABLE},
+ {"syscall", KIT_CG_INTRIN_SYSCALL},
+ {"coro_switch", KIT_CG_INTRIN_CORO_SWITCH},
+ };
+ uint64_t v;
+ if (!toy_parse_dot_const(p, rows, sizeof rows / sizeof rows[0], 1,
+ "intrinsic name", "intrinsic", &v))
return 0;
- }
+ *out = (KitCgIntrinsic)v;
return 1;
}
int toy_parse_rounding_const(ToyParser* p, KitCgRounding* out) {
- KitSym name;
- if (!toy_parse_attr_dot_name(p, &name)) return 0;
- if (toy_sym_is(p, name, "default"))
- *out = KIT_CG_ROUND_DEFAULT;
- else if (toy_sym_is(p, name, "nearest_even"))
- *out = KIT_CG_ROUND_NEAREST_EVEN;
- else if (toy_sym_is(p, name, "toward_zero"))
- *out = KIT_CG_ROUND_TOWARD_ZERO;
- else if (toy_sym_is(p, name, "down"))
- *out = KIT_CG_ROUND_DOWN;
- else if (toy_sym_is(p, name, "up"))
- *out = KIT_CG_ROUND_UP;
- else {
- toy_error(p, p->cur.loc, "unknown rounding mode");
+ static const ToyConstRow rows[] = {
+ {"default", KIT_CG_ROUND_DEFAULT},
+ {"nearest_even", KIT_CG_ROUND_NEAREST_EVEN},
+ {"toward_zero", KIT_CG_ROUND_TOWARD_ZERO},
+ {"down", KIT_CG_ROUND_DOWN},
+ {"up", KIT_CG_ROUND_UP},
+ };
+ uint64_t v;
+ if (!toy_parse_dot_const(p, rows, sizeof rows / sizeof rows[0], 0, NULL,
+ "rounding mode", &v))
return 0;
- }
+ *out = (KitCgRounding)v;
return 1;
}
diff --git a/lang/toy/internal.h b/lang/toy/internal.h
@@ -319,6 +319,38 @@ void toy_error(ToyParser* p, KitSrcLoc loc, const char* fmt, ...);
void toy_set_loc(ToyParser* p);
KitSym toy_tok_sym(ToyParser* p, ToyToken tok);
int toy_sym_is(ToyParser* p, KitSym sym, const char* name);
+
+/* A single ".name" -> value row for the dot-const tables. The value is widened
+ * to uint64_t so one table type serves every enum/flag domain; thin typed
+ * wrappers narrow it back at the call site. */
+typedef struct {
+ const char* name;
+ uint64_t value;
+} ToyConstRow;
+
+/* Map an already-interned token sym against a const table, interning each row
+ * name once. On a hit, sets *out and returns 1; on a miss, raises
+ * "unknown <what>" and returns 0. */
+int toy_lookup_const(ToyParser* p, KitSym tok, const ToyConstRow* rows, size_t n,
+ const char* what, uint64_t* out);
+
+/* Parse a ".name" dot-const and resolve it via `rows`. When `strict_ident` is
+ * set the name must be a bare identifier ("expected <expected>" on failure);
+ * otherwise keyword tokens are also accepted (via toy_parse_attr_dot_name). On
+ * an unknown name, raises "unknown <unknown>". */
+int toy_parse_dot_const(ToyParser* p, const ToyConstRow* rows, size_t n,
+ int strict_ident, const char* expected,
+ const char* unknown, uint64_t* out);
+
+/* Parse a comma-separated run of ".name" flags, OR-ing each matched row value
+ * into *mask. When `comma_prefixed` is set, every item must be introduced by a
+ * comma and the loop stops at the first non-comma (the mem-flags tail form).
+ * Otherwise the run continues until ')' or EOF with comma separators (the asm
+ * group form); the caller owns the surrounding parentheses. Unknown names raise
+ * "unknown <unknown>". */
+int toy_parse_flag_set(ToyParser* p, const ToyConstRow* rows, size_t n,
+ const char* unknown, int comma_prefixed, uint32_t* mask);
+
int toy_expect_comma(ToyParser* p);
int toy_skip_attr_list_ex(ToyParser* p, int* has_static);
int toy_skip_attr_list(ToyParser* p);
diff --git a/lang/toy/parser_core.c b/lang/toy/parser_core.c
@@ -416,6 +416,55 @@ int toy_sym_is(ToyParser* p, KitSym sym, const char* name) {
return sym == kit_sym_intern(p->c, kit_slice_cstr(name));
}
+int toy_lookup_const(ToyParser* p, KitSym tok, const ToyConstRow* rows, size_t n,
+ const char* what, uint64_t* out) {
+ size_t i;
+ for (i = 0; i < n; ++i) {
+ if (tok == kit_sym_intern(p->c, kit_slice_cstr(rows[i].name))) {
+ *out = rows[i].value;
+ return 1;
+ }
+ }
+ toy_error(p, p->cur.loc, "unknown %s", what);
+ return 0;
+}
+
+int toy_parse_dot_const(ToyParser* p, const ToyConstRow* rows, size_t n,
+ int strict_ident, const char* expected,
+ const char* unknown, uint64_t* out) {
+ KitSym tok;
+ if (strict_ident) {
+ if (!toy_parser_expect(p, TOK_DOT) || p->cur.kind != TOK_IDENT) {
+ toy_error(p, p->cur.loc, "expected %s", expected);
+ return 0;
+ }
+ tok = toy_tok_sym(p, p->cur);
+ toy_parser_advance(p);
+ } else {
+ (void)expected;
+ if (!toy_parse_attr_dot_name(p, &tok)) return 0;
+ }
+ return toy_lookup_const(p, tok, rows, n, unknown, out);
+}
+
+int toy_parse_flag_set(ToyParser* p, const ToyConstRow* rows, size_t n,
+ const char* unknown, int comma_prefixed, uint32_t* mask) {
+ for (;;) {
+ KitSym name;
+ uint64_t value;
+ if (comma_prefixed) {
+ if (!toy_parser_match(p, TOK_COMMA)) break;
+ } else if (p->cur.kind == TOK_RPAREN || p->cur.kind == TOK_EOF) {
+ break;
+ }
+ if (!toy_parse_attr_dot_name(p, &name)) return 0;
+ if (!toy_lookup_const(p, name, rows, n, unknown, &value)) return 0;
+ *mask |= (uint32_t)value;
+ if (!comma_prefixed && !toy_parser_match(p, TOK_COMMA)) break;
+ }
+ return 1;
+}
+
int toy_skip_attr_list_ex(ToyParser* p, int* has_static) {
int bracket_depth = 0;
int paren_depth = 0;