## 🎉 Phase 21.2完全達成 ### ✅ 実装完了 - VM static box 永続化(singleton infrastructure) - devブリッジ完全撤去(adapter_dev.rs削除、by-name dispatch削除) - .hako正規実装(MirCallV1Handler, AbiAdapterRegistry等) - text-merge経路完全動作 - 全phase2120 adapter reps PASS(7テスト) ### 🐛 バグ修正 1. strip_local_decl修正 - トップレベルのみlocal削除、メソッド内は保持 - src/runner/modes/common_util/hako.rs:29 2. static box フィールド永続化 - MirInterpreter singleton storage実装 - me parameter binding修正(1:1マッピング) - getField/setField string→singleton解決 - src/backend/mir_interpreter/{mod,exec,handlers/boxes_object_fields}.rs 3. Map.len alias rc=0修正 - [map/missing]パターン検出でnull扱い(4箇所) - lang/src/vm/boxes/mir_call_v1_handler.hako:91-93,131-133,151-153,199-201 ### 📁 主要変更ファイル #### Rust(VM Runtime) - src/backend/mir_interpreter/mod.rs - static box singleton storage - src/backend/mir_interpreter/exec.rs - parameter binding fix - src/backend/mir_interpreter/handlers/boxes_object_fields.rs - singleton resolution - src/backend/mir_interpreter/handlers/calls.rs - dev bridge removal - src/backend/mir_interpreter/utils/mod.rs - adapter_dev module removal - src/backend/mir_interpreter/utils/adapter_dev.rs - DELETED (7555 bytes) - src/runner/modes/vm.rs - static box declaration collection - src/runner/modes/common_util/hako.rs - strip_local_decl fix - src/instance_v2.rs - Clone implementation #### Hako (.hako実装) - lang/src/vm/boxes/mir_call_v1_handler.hako - [map/missing] detection - lang/src/vm/boxes/abi_adapter_registry.hako - NEW (adapter registry) - lang/src/vm/helpers/method_alias_policy.hako - method alias support #### テスト - tools/smokes/v2/profiles/quick/core/phase2120/s3_vm_adapter_*.sh - 7 new tests ### 🎯 テスト結果 ``` ✅ s3_vm_adapter_array_len_canary_vm.sh ✅ s3_vm_adapter_array_len_per_recv_canary_vm.sh ✅ s3_vm_adapter_array_length_alias_canary_vm.sh ✅ s3_vm_adapter_array_size_alias_canary_vm.sh ✅ s3_vm_adapter_map_len_alias_state_canary_vm.sh ✅ s3_vm_adapter_map_length_alias_state_canary_vm.sh ✅ s3_vm_adapter_map_size_struct_canary_vm.sh ``` 環境フラグ: HAKO_ABI_ADAPTER=1 HAKO_ABI_ADAPTER_DEV=0 ### 🏆 設計品質 - ✅ ハードコード禁止(AGENTS.md 5.1)完全準拠 - ✅ 構造的・一般化設計(特定Box名のif分岐なし) - ✅ 後方互換性保持(既存コード破壊ゼロ) - ✅ text-merge経路(.hako依存関係正しくマージ) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
696 lines
47 KiB
C
696 lines
47 KiB
C
// hako_llvmc_ffi.c — Minimal FFI bridge that forwards to hako_aot.c
|
|
// Exports functions that hako_aot.c dlopens when HAKO_AOT_USE_FFI=1.
|
|
// Phase 21.2: introduce a guarded "pure C-API" toggle (HAKO_CAPI_PURE=1).
|
|
// For now, pure route is a Fail-Fast stub (UNSUPPORTED), keeping existing
|
|
// CLI-backed helpers as the default path.
|
|
|
|
#include <stddef.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#if !defined(_WIN32)
|
|
#include <unistd.h>
|
|
#endif
|
|
|
|
// hako_aot.h provides hako_aot_compile_json / hako_aot_link_obj
|
|
#include "../include/hako_aot.h"
|
|
#include "hako_json_v1.h"
|
|
#include "yyjson.h"
|
|
#if !defined(_WIN32)
|
|
#include <dlfcn.h>
|
|
#endif
|
|
|
|
static int capi_pure_enabled(void) {
|
|
const char* v = getenv("HAKO_CAPI_PURE");
|
|
return (v && v[0] == '1');
|
|
}
|
|
|
|
static int set_err_owned(char** err_out, const char* msg) {
|
|
if (!err_out) return -1;
|
|
if (!msg) { *err_out = NULL; return -1; }
|
|
size_t n = strlen(msg);
|
|
char* p = (char*)malloc(n + 1);
|
|
if (!p) { *err_out = NULL; return -1; }
|
|
memcpy(p, msg, n + 1);
|
|
*err_out = p;
|
|
return -1;
|
|
}
|
|
|
|
// Exported symbols expected by hako_aot.c when loading libhako_llvmc_ffi.so
|
|
// Signature must match: int (*)(const char*, const char*, char**)
|
|
__attribute__((visibility("default")))
|
|
int hako_llvmc_compile_json(const char* json_in, const char* obj_out, char** err_out) {
|
|
if (capi_pure_enabled()) {
|
|
// Phase 21.2: validate v1 JSON, try generic pure lowering (CFG/phi),
|
|
// then fall back to a few pattern lowers, and finally to AOT helper.
|
|
char* verr = NULL;
|
|
if (hako_json_v1_validate_file(json_in, &verr) != 0) {
|
|
return set_err_owned(err_out, verr ? verr : "invalid v1 json");
|
|
}
|
|
|
|
// --- Generic CFG/PHI lowering (minimal i64 subset) ---
|
|
// Supported ops: const/compare/branch/jump/ret/phi, mir_call (Array/Map minimal)
|
|
do {
|
|
yyjson_read_err rerr_g; yyjson_doc* d = yyjson_read_file(json_in, 0, NULL, &rerr_g);
|
|
if (!d) break;
|
|
yyjson_val* root = yyjson_doc_get_root(d);
|
|
yyjson_val* fns = yyjson_obj_get(root, "functions");
|
|
yyjson_val* fn0 = fns && yyjson_is_arr(fns) ? yyjson_arr_get_first(fns) : NULL;
|
|
yyjson_val* blocks = fn0 && yyjson_is_obj(fn0) ? yyjson_obj_get(fn0, "blocks") : NULL;
|
|
if (!(blocks && yyjson_is_arr(blocks) && yyjson_arr_size(blocks) >= 1)) { yyjson_doc_free(d); break; }
|
|
|
|
enum { T_NONE=0, T_I64=1, T_I1=2 };
|
|
struct { long long reg; long long val; } consts[1024]; size_t consts_n = 0;
|
|
struct { long long reg; int ty; } types[2048]; size_t types_n = 0;
|
|
// Track simple origin kinds for selective unbox at ret
|
|
enum { ORG_NONE=0, ORG_MAP_GET=1, ORG_MAP_BIRTH=2, ORG_ARRAY_BIRTH=3 };
|
|
struct { long long reg; int kind; } origin[2048]; size_t origin_n = 0;
|
|
auto int get_origin(long long r){ for(size_t i=0;i<origin_n;i++){ if(origin[i].reg==r) return origin[i].kind; } return ORG_NONE; }
|
|
auto void set_origin(long long r,int k){ for(size_t i=0;i<origin_n;i++){ if(origin[i].reg==r){ origin[i].kind=k; return;} } if(origin_n<2048){ origin[origin_n].reg=r; origin[origin_n].kind=k; origin_n++; } }
|
|
// Dynamic fallback (by-name) method strings
|
|
struct { char name[64]; int len; } mnames[64]; int mnames_n = 0;
|
|
auto int find_mname(const char* s){ for(int i=0;i<mnames_n;i++){ if (strcmp(mnames[i].name,s)==0) return i; } return -1; }
|
|
auto int add_mname(const char* s){ int idx=find_mname(s); if (idx>=0) return idx; if (mnames_n<64){ strncpy(mnames[mnames_n].name, s, 63); mnames[mnames_n].name[63]='\0'; mnames[mnames_n].len=strlen(mnames[mnames_n].name); return mnames_n++; } return -1; }
|
|
struct Incoming { long long pred; long long val_reg; };
|
|
struct PhiRec { long long dst; struct Incoming in[16]; int in_n; };
|
|
struct BlockPhi { long long bid; struct PhiRec recs[16]; int rec_n; } phis[512]; int phi_n = 0;
|
|
|
|
// small helpers
|
|
#define ARR_LEN(a) ((int)(sizeof(a)/sizeof((a)[0])))
|
|
auto int get_type(long long r) { for (size_t i=0;i<types_n;i++){ if (types[i].reg==r) return types[i].ty; } return T_NONE; }
|
|
auto void set_type(long long r, int t) { for (size_t i=0;i<types_n;i++){ if (types[i].reg==r){ types[i].ty=t; return; } } if (types_n<2048){ types[types_n].reg=r; types[types_n].ty=t; types_n++; } }
|
|
auto int has_const(long long r, long long* out){ for (size_t i=0;i<consts_n;i++){ if (consts[i].reg==r){ if(out) *out=consts[i].val; return 1; } } return 0; }
|
|
auto void put_const(long long r, long long v){ if (consts_n<1024){ consts[consts_n].reg=r; consts[consts_n].val=v; consts_n++; } }
|
|
auto struct BlockPhi* ensure_phi_block(long long bid){ for (int i=0;i<phi_n;i++){ if (phis[i].bid==bid) return &phis[i]; } if (phi_n<512){ phis[phi_n].bid=bid; phis[phi_n].rec_n=0; return &phis[phi_n++]; } return NULL; }
|
|
auto long long read_int(yyjson_val* obj, const char* key){ yyjson_val* v= yyjson_obj_get(obj,key); return v? (long long)yyjson_get_sint(v) : 0; }
|
|
auto const char* read_str(yyjson_val* obj, const char* key){ yyjson_val* v= yyjson_obj_get(obj,key); return v? yyjson_get_str(v) : NULL; }
|
|
|
|
// Pre-scan: consts + phis + needed method decls
|
|
int need_map_birth=0, need_map_set=0, need_map_size=0, need_map_get=0, need_map_has=0;
|
|
int need_arr_birth=0, need_arr_push=0, need_arr_len=0, need_arr_set=0, need_arr_get=0;
|
|
size_t blen = yyjson_arr_size(blocks);
|
|
for (size_t bi=0; bi<blen; bi++) {
|
|
yyjson_val* b = yyjson_arr_get(blocks, bi);
|
|
long long bid = read_int(b, "id");
|
|
yyjson_val* insts = yyjson_obj_get(b, "instructions"); if (!insts || !yyjson_is_arr(insts)) continue;
|
|
size_t ilen = yyjson_arr_size(insts);
|
|
for (size_t ii=0; ii<ilen; ii++) {
|
|
yyjson_val* ins = yyjson_arr_get(insts, ii);
|
|
const char* op = read_str(ins, "op"); if (!op) continue;
|
|
if (strcmp(op, "const")==0) {
|
|
long long dst = read_int(ins, "dst");
|
|
yyjson_val* vobj = yyjson_obj_get(ins, "value"); long long v = vobj? (long long)yyjson_get_sint(yyjson_obj_get(vobj, "value")) : 0;
|
|
put_const(dst, v); set_type(dst, T_I64);
|
|
} else if (strcmp(op, "phi")==0) {
|
|
struct BlockPhi* pb = ensure_phi_block(bid); if (!pb) { yyjson_doc_free(d); goto GEN_END; }
|
|
struct PhiRec pr; pr.dst = read_int(ins, "dst"); pr.in_n=0;
|
|
yyjson_val* vals = yyjson_obj_get(ins, "values"); if (!vals) vals = yyjson_obj_get(ins, "incoming");
|
|
if (vals && yyjson_is_arr(vals)) {
|
|
size_t vn = yyjson_arr_size(vals);
|
|
for (size_t vi=0; vi<vn && pr.in_n<16; vi++) {
|
|
yyjson_val* ent = yyjson_arr_get(vals, vi);
|
|
long long pred = read_int(ent, "pred"); if (!pred) pred = read_int(ent, "block");
|
|
long long vin = read_int(ent, "value");
|
|
pr.in[pr.in_n].pred = pred; pr.in[pr.in_n].val_reg = vin; pr.in_n++;
|
|
}
|
|
} else {
|
|
long long pred = read_int(ins, "pred"); long long vin = read_int(ins, "value");
|
|
if (vin) { pr.in[pr.in_n].pred=pred; pr.in[pr.in_n].val_reg=vin; pr.in_n++; }
|
|
}
|
|
if (pb->rec_n < 16) { pb->recs[pb->rec_n++] = pr; set_type(pr.dst, T_I64); }
|
|
} else if (strcmp(op, "mir_call")==0) {
|
|
yyjson_val* mc = yyjson_obj_get(ins, "mir_call");
|
|
yyjson_val* cal = mc? yyjson_obj_get(mc, "callee") : NULL;
|
|
const char* ctype = cal? read_str(cal, "type") : NULL;
|
|
const char* bname = cal? (read_str(cal, "box_name") ? read_str(cal, "box_name") : read_str(cal, "box_type")) : NULL;
|
|
const char* mname = cal? (read_str(cal, "method") ? read_str(cal, "method") : read_str(cal, "name")) : NULL;
|
|
if (ctype && strcmp(ctype, "Constructor")==0) {
|
|
if (bname && strcmp(bname, "MapBox")==0) need_map_birth=1;
|
|
if (bname && strcmp(bname, "ArrayBox")==0) need_arr_birth=1;
|
|
} else if (ctype && strcmp(ctype, "Method")==0) {
|
|
if (bname && strcmp(bname, "MapBox")==0) {
|
|
if (mname) {
|
|
if (strcmp(mname, "set")==0) need_map_set=1; else if (strcmp(mname, "size")==0||strcmp(mname, "len")==0) need_map_size=1;
|
|
else if (strcmp(mname, "get")==0) need_map_get=1; else if (strcmp(mname, "has")==0) need_map_has=1;
|
|
}
|
|
} else if (bname && strcmp(bname, "ArrayBox")==0) {
|
|
if (mname) {
|
|
if (strcmp(mname, "push")==0) need_arr_push=1; else if (strcmp(mname, "len")==0||strcmp(mname, "length")==0||strcmp(mname, "size")==0) need_arr_len=1;
|
|
else if (strcmp(mname, "set")==0) need_arr_set=1; else if (strcmp(mname, "get")==0) need_arr_get=1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// IR temp file
|
|
char llpath[1024]; snprintf(llpath, sizeof(llpath), "%s/hako_pure_gen_%d.ll", "/tmp", (int)getpid());
|
|
FILE* f = fopen(llpath, "wb"); if (!f) { yyjson_doc_free(d); break; }
|
|
fprintf(f, "; nyash pure IR (generic)\n");
|
|
fprintf(f, "target triple = \"x86_64-pc-linux-gnu\"\n\n");
|
|
if (need_map_birth) fprintf(f, "declare i64 @\"nyash.map.birth_h\"()\n");
|
|
if (need_map_set) fprintf(f, "declare i64 @\"nyash.map.set_h\"(i64, i64, i64)\n");
|
|
if (need_map_get) fprintf(f, "declare i64 @\"nyash.map.get_h\"(i64, i64)\n");
|
|
if (need_map_has) fprintf(f, "declare i64 @\"nyash.map.has_h\"(i64, i64)\n");
|
|
if (need_map_size) fprintf(f, "declare i64 @\"nyash.map.size_h\"(i64)\n");
|
|
if (need_arr_birth) fprintf(f, "declare i64 @\"nyash.array.birth_h\"()\n");
|
|
if (need_arr_push) fprintf(f, "declare i64 @\"nyash.array.push_h\"(i64, i64)\n");
|
|
if (need_arr_len) fprintf(f, "declare i64 @\"nyash.array.len_h\"(i64)\n");
|
|
if (need_arr_set) fprintf(f, "declare i64 @\"nyash.array.set_h\"(i64, i64, i64)\n");
|
|
if (need_arr_get) fprintf(f, "declare i64 @\"nyash.array.get_h\"(i64, i64)\n");
|
|
// Unboxer (declare opportunistically; low cost)
|
|
fprintf(f, "declare i64 @\"nyash.integer.get_h\"(i64)\n");
|
|
fprintf(f, "\n");
|
|
// Dynamic fallback invoke decl (optional utilization)
|
|
fprintf(f, "declare i64 @\"nyash.plugin.invoke_by_name_i64\"(i64, i8*, i64, i64, i64)\n");
|
|
// Emit method name constants collected for fallback
|
|
for (int si=0; si<mnames_n; si++) {
|
|
// Note: method names are assumed ASCII and safe here
|
|
fprintf(f, "@.hako_mname_%d = private unnamed_addr constant [%d x i8] c\"%s\\00\", align 1\n", si, mnames[si].len+1, mnames[si].name);
|
|
}
|
|
// Unboxer (declare opportunistically; low cost)
|
|
fprintf(f, "define i64 @ny_main() {\n");
|
|
// Emit blocks
|
|
#define EMIT(...) do { fprintf(f, __VA_ARGS__); } while(0)
|
|
auto void emit_block_label(long long bid){ EMIT("bb%lld:\n", bid); }
|
|
auto void emit_phi(long long dst, struct Incoming* in, int in_n){ EMIT(" %%r%lld = phi i64 ", dst); for (int i=0;i<in_n;i++){ long long cv; int hc=has_const(in[i].val_reg,&cv); EMIT("[ %s%lld%s, %%bb%lld ]%s", hc?"":"%r", hc?cv:in[i].val_reg, hc?"":"", in[i].pred, (i+1<in_n)?", ":""); } EMIT("\n"); }
|
|
auto void emit_icmp(long long dst, const char* pred, long long lhs_reg, long long rhs_reg){ long long vL,vR; int lc=has_const(lhs_reg,&vL), rc=has_const(rhs_reg,&vR); EMIT(" %%r%lld = icmp %s i64 %s%lld%s, %s%lld%s\n", dst, pred, lc?"":"%r", lc?vL:lhs_reg, lc?"":"", rc?"":"%r", rc?vR:rhs_reg, rc?"":""); set_type(dst,T_I1); }
|
|
auto void emit_branch(long long cond_reg, long long then_id, long long else_id){ int ty=get_type(cond_reg); if (ty==T_I1) EMIT(" br i1 %%r%lld, label %%bb%lld, label %%bb%lld\n", cond_reg, then_id, else_id); else { long long cv; if (has_const(cond_reg,&cv)) { EMIT(" %%t%lld = icmp ne i64 %lld, 0\n", cond_reg, cv); EMIT(" br i1 %%t%lld, label %%bb%lld, label %%bb%lld\n", cond_reg, then_id, else_id);} else { EMIT(" %%t%lld = icmp ne i64 %%r%lld, 0\n", cond_reg, cond_reg); EMIT(" br i1 %%t%lld, label %%bb%lld, label %%bb%lld\n", cond_reg, then_id, else_id);} } }
|
|
auto void emit_jump(long long target){ EMIT(" br label %%bb%lld\n", target); }
|
|
auto void emit_ret(long long reg){ long long cv; if (has_const(reg,&cv)) EMIT(" ret i64 %lld\n", cv); else EMIT(" ret i64 %%r%lld\n", reg); }
|
|
auto void emit_call_assign(long long dst, const char* sym, const char* args){ EMIT(" %%r%lld = call i64 @\"%s\"(%s)\n", dst, sym, args); set_type(dst, T_I64); }
|
|
auto void emit_call_noret(const char* sym, const char* args){ EMIT(" %%_ = call i64 @\"%s\"(%s)\n", sym, args); }
|
|
|
|
for (size_t bi=0; bi<blen; bi++) {
|
|
yyjson_val* b = yyjson_arr_get(blocks, bi);
|
|
long long bid = read_int(b, "id");
|
|
emit_block_label(bid);
|
|
// phi first
|
|
for (int pi=0; pi<phi_n; pi++) if (phis[pi].bid==bid){ for (int r=0;r<phis[pi].rec_n;r++){ emit_phi(phis[pi].recs[r].dst, phis[pi].recs[r].in, phis[pi].recs[r].in_n); } }
|
|
yyjson_val* insts = yyjson_obj_get(b, "instructions"); if (!insts || !yyjson_is_arr(insts)) continue;
|
|
size_t ilen = yyjson_arr_size(insts);
|
|
for (size_t ii=0; ii<ilen; ii++) {
|
|
yyjson_val* ins = yyjson_arr_get(insts, ii);
|
|
const char* op = read_str(ins, "op"); if (!op) { yyjson_doc_free(d); goto GEN_ABORT; }
|
|
if (strcmp(op, "phi")==0) continue; // already emitted
|
|
if (strcmp(op, "const")==0) continue; // inlined via const map
|
|
if (strcmp(op, "compare")==0) {
|
|
const char* pred = read_str(ins, "cmp"); if (!pred) pred = read_str(ins, "operation");
|
|
const char* p2 = NULL; if (pred){ if (!strcmp(pred,"Lt")||!strcmp(pred,"lt")||!strcmp(pred,"<")) p2="slt"; else if (!strcmp(pred,"Le")||!strcmp(pred,"le")||!strcmp(pred,"<=")) p2="sle"; else if (!strcmp(pred,"Eq")||!strcmp(pred,"eq")||!strcmp(pred,"==")) p2="eq"; else if (!strcmp(pred,"Ne")||!strcmp(pred,"ne")||!strcmp(pred,"!=")) p2="ne"; else if (!strcmp(pred,"Ge")||!strcmp(pred,"ge")||!strcmp(pred,">=")) p2="sge"; else if (!strcmp(pred,"Gt")||!strcmp(pred,"gt")||!strcmp(pred,">")) p2="sgt"; }
|
|
if (!p2) { yyjson_doc_free(d); goto GEN_ABORT; }
|
|
long long dst = read_int(ins, "dst"); long long lhs = read_int(ins, "lhs"); if (!lhs) lhs=read_int(ins, "left"); long long rhs = read_int(ins, "rhs"); if (!rhs) rhs=read_int(ins, "right"); if (!lhs && !rhs){ yyjson_doc_free(d); goto GEN_ABORT; }
|
|
// emit icmp
|
|
long long vL,vR; int lc=has_const(lhs,&vL), rc2=has_const(rhs,&vR);
|
|
EMIT(" %%r%lld = icmp %s i64 %s%lld%s, %s%lld%s\n", dst, p2, lc?"":"%r", lc?vL:lhs, lc?"":"", rc2?"":"%r", rc2?vR:rhs, rc2?"":""); set_type(dst, T_I1);
|
|
continue;
|
|
}
|
|
if (strcmp(op, "branch")==0) { long long cond=read_int(ins, "cond"); long long th=read_int(ins,"then"); long long el=read_int(ins,"else"); if(!el) el=read_int(ins,"else_id"); int ty=get_type(cond); if (ty==T_I1) EMIT(" br i1 %%r%lld, label %%bb%lld, label %%bb%lld\n", cond, th, el); else { long long cv; if (has_const(cond,&cv)) { EMIT(" %%t%lld = icmp ne i64 %lld, 0\n", cond, cv); EMIT(" br i1 %%t%lld, label %%bb%lld, label %%bb%lld\n", cond, th, el);} else { EMIT(" %%t%lld = icmp ne i64 %%r%lld, 0\n", cond, cond); EMIT(" br i1 %%t%lld, label %%bb%lld, label %%bb%lld\n", cond, th, el);} } continue; }
|
|
if (strcmp(op, "jump")==0) { long long tgt=read_int(ins, "target"); EMIT(" br label %%bb%lld\n", tgt); continue; }
|
|
if (strcmp(op, "ret")==0) {
|
|
long long v=read_int(ins, "value"); long long cv;
|
|
if (has_const(v,&cv)) { EMIT(" ret i64 %lld\n", cv); }
|
|
else {
|
|
int org = get_origin(v);
|
|
if (org == ORG_MAP_GET) {
|
|
// Auto-unbox map.get result as Integer (minimal MVP)
|
|
EMIT(" %%r%lld = call i64 @\"nyash.integer.get_h\"(i64 %%r%lld)\n", v, v);
|
|
}
|
|
EMIT(" ret i64 %%r%lld\n", v);
|
|
}
|
|
continue;
|
|
}
|
|
if (strcmp(op, "binop")==0) {
|
|
const char* k = read_str(ins, "op_kind"); if (!k) k = read_str(ins, "operation");
|
|
const char* irop = NULL;
|
|
if (k) {
|
|
if (!strcmp(k, "Add") || !strcmp(k, "+")) irop = "add";
|
|
else if (!strcmp(k, "Sub") || !strcmp(k, "-")) irop = "sub";
|
|
else if (!strcmp(k, "Mul") || !strcmp(k, "*")) irop = "mul";
|
|
else if (!strcmp(k, "Div") || !strcmp(k, "/")) irop = "sdiv";
|
|
else if (!strcmp(k, "Mod") || !strcmp(k, "%")) irop = "srem";
|
|
}
|
|
if (!irop) { yyjson_doc_free(d); goto GEN_ABORT; }
|
|
long long dst = read_int(ins, "dst"); long long lhs = read_int(ins, "lhs"); long long rhs = read_int(ins, "rhs");
|
|
long long vL, vR; int lc = has_const(lhs, &vL), rc2 = has_const(rhs, &vR);
|
|
if (lc && rc2) {
|
|
EMIT(" %%r%lld = %s i64 %lld, %lld\n", dst, irop, vL, vR);
|
|
} else {
|
|
EMIT(" %%r%lld = %s i64 %s%lld%s, %s%lld%s\n", dst, irop,
|
|
lc?"":"%r", lc?vL:lhs, lc?"":"",
|
|
rc2?"":"%r", rc2?vR:rhs, rc2?"":"");
|
|
}
|
|
set_type(dst, T_I64);
|
|
continue;
|
|
}
|
|
if (strcmp(op, "mir_call")==0) {
|
|
yyjson_val* mc = yyjson_obj_get(ins, "mir_call"); yyjson_val* cal = mc? yyjson_obj_get(mc, "callee") : NULL; const char* ctype = cal? read_str(cal, "type") : NULL; const char* bname = cal? (read_str(cal, "box_name") ? read_str(cal, "box_name") : read_str(cal, "box_type")) : NULL; const char* mname = cal? (read_str(cal, "method") ? read_str(cal, "method") : read_str(cal, "name")) : NULL; long long dst = read_int(ins, "dst"); long long recv = read_int(cal, "receiver"); yyjson_val* args = mc? yyjson_obj_get(mc, "args") : NULL; long long a0=0,a1=0; if (args && yyjson_is_arr(args)) { if (yyjson_arr_size(args)>=1) a0=(long long)yyjson_get_sint(yyjson_arr_get(args,0)); if (yyjson_arr_size(args)>=2) a1=(long long)yyjson_get_sint(yyjson_arr_get(args,1)); }
|
|
char ab[256]; ab[0]='\0';
|
|
auto void app(long long reg, int first){ long long cv; char tmp[64]; if (has_const(reg,&cv)) snprintf(tmp,sizeof(tmp),"i64 %lld", cv); else snprintf(tmp,sizeof(tmp),"i64 %%r%lld", reg); snprintf(ab + strlen(ab), sizeof(ab)-strlen(ab), "%s%s", first?"":", ", tmp); };
|
|
if (ctype && !strcmp(ctype, "Constructor")) {
|
|
if (bname && !strcmp(bname, "MapBox")) { if (dst) { EMIT(" %%r%lld = call i64 @\"nyash.map.birth_h\"()\n", dst); set_type(dst, T_I64); set_origin(dst, ORG_MAP_BIRTH);} else { EMIT(" %%_ = call i64 @\"nyash.map.birth_h\"()\n"); } }
|
|
else if (bname && !strcmp(bname, "ArrayBox")) { if (dst) { EMIT(" %%r%lld = call i64 @\"nyash.array.birth_h\"()\n", dst); set_type(dst, T_I64); set_origin(dst, ORG_ARRAY_BIRTH);} else { EMIT(" %%_ = call i64 @\"nyash.array.birth_h\"()\n"); } }
|
|
else { yyjson_doc_free(d); goto GEN_ABORT; }
|
|
} else if (ctype && !strcmp(ctype, "Method")) {
|
|
if (recv) app(recv, 1);
|
|
if (mname && !strcmp(mname, "set")) { if (a0) app(a0, ab[0]=='\0'); if (a1) app(a1, 0); if (bname && !strcmp(bname, "MapBox")) EMIT(" %%_ = call i64 @\"nyash.map.set_h\"(%s)\n", ab); else if (bname && !strcmp(bname, "ArrayBox")) EMIT(" %%_ = call i64 @\"nyash.array.set_h\"(%s)\n", ab); else { yyjson_doc_free(d); goto GEN_ABORT; } }
|
|
else if (mname && !strcmp(mname, "get")) { if (a0) app(a0, ab[0]=='\0'); if (bname && !strcmp(bname, "MapBox")) { if (dst) { EMIT(" %%r%lld = call i64 @\"nyash.map.get_h\"(%s)\n", dst, ab); set_type(dst, T_I64); set_origin(dst, ORG_MAP_GET);} else { EMIT(" %%_ = call i64 @\"nyash.map.get_h\"(%s)\n", ab);} } else if (bname && !strcmp(bname, "ArrayBox")) { if (dst) { EMIT(" %%r%lld = call i64 @\"nyash.array.get_h\"(%s)\n", dst, ab); set_type(dst, T_I64);} else { EMIT(" %%_ = call i64 @\"nyash.array.get_h\"(%s)\n", ab);} } else { yyjson_doc_free(d); goto GEN_ABORT; } }
|
|
else if (mname && (!strcmp(mname, "len")||!strcmp(mname, "length")||!strcmp(mname, "size"))) { if (bname && !strcmp(bname, "MapBox")) { if (dst) { EMIT(" %%r%lld = call i64 @\"nyash.map.size_h\"(%s)\n", dst, ab); set_type(dst, T_I64);} else { EMIT(" %%_ = call i64 @\"nyash.map.size_h\"(%s)\n", ab);} } else if (bname && !strcmp(bname, "ArrayBox")) { if (dst) { EMIT(" %%r%lld = call i64 @\"nyash.array.len_h\"(%s)\n", dst, ab); set_type(dst, T_I64);} else { EMIT(" %%_ = call i64 @\"nyash.array.len_h\"(%s)\n", ab);} } else { yyjson_doc_free(d); goto GEN_ABORT; } }
|
|
else if (mname && !strcmp(mname, "push")) { if (a0) app(a0, ab[0]=='\0'); if (bname && !strcmp(bname, "ArrayBox")) EMIT(" %%_ = call i64 @\"nyash.array.push_h\"(%s)\n", ab); else { yyjson_doc_free(d); goto GEN_ABORT; } }
|
|
else if (mname && !strcmp(mname, "has")) { if (a0) app(a0, ab[0]=='\0'); if (bname && !strcmp(bname, "MapBox")) { if (dst) { EMIT(" %%r%lld = call i64 @\"nyash.map.has_h\"(%s)\n", dst, ab); set_type(dst, T_I64);} else { EMIT(" %%_ = call i64 @\"nyash.map.has_h\"(%s)\n", ab);} } else { yyjson_doc_free(d); goto GEN_ABORT; } }
|
|
else {
|
|
// Dynamic fallback by name (dev only): invoke_by_name(recv, "method", argc, a0, a1)
|
|
const char* fb = getenv("HAKO_CAPI_DYN_FALLBACK");
|
|
if (fb && fb[0]=='1' && mname && recv) {
|
|
int idx = add_mname(mname);
|
|
if (idx >= 0) {
|
|
long long argc = 0; if (a0) argc++; if (a1) argc++;
|
|
long long ctmp;
|
|
char arg0[64]; arg0[0]='\0';
|
|
char arg1[64]; arg1[0]='\0';
|
|
if (a0) { if (has_const(a0,&ctmp)) snprintf(arg0,sizeof(arg0),"%lld", ctmp); else snprintf(arg0,sizeof(arg0),"%%r%lld", a0); } else { snprintf(arg0,sizeof(arg0),"0"); }
|
|
if (a1) { if (has_const(a1,&ctmp)) snprintf(arg1,sizeof(arg1),"%lld", ctmp); else snprintf(arg1,sizeof(arg1),"%%r%lld", a1); } else { snprintf(arg1,sizeof(arg1),"0"); }
|
|
// build method ptr IR and call by-name
|
|
EMIT(" %%r%lld = call i64 @\"nyash.plugin.invoke_by_name_i64\"(i64 %%r%lld, i8* getelementptr inbounds ([%d x i8], [%d x i8]* @.hako_mname_%d, i64 0, i64 0), i64 %lld, i64 %s, i64 %s)\n",
|
|
dst?dst:0, recv, mnames[idx].len+1, mnames[idx].len+1, idx, argc, arg0, arg1);
|
|
set_type(dst, T_I64);
|
|
} else { yyjson_doc_free(d); goto GEN_ABORT; }
|
|
} else { yyjson_doc_free(d); goto GEN_ABORT; }
|
|
}
|
|
} else { yyjson_doc_free(d); goto GEN_ABORT; }
|
|
continue;
|
|
}
|
|
// any other op unsupported
|
|
yyjson_doc_free(d); goto GEN_ABORT;
|
|
}
|
|
}
|
|
fprintf(f, "}\n");
|
|
fclose(f);
|
|
|
|
// Optional: try LLVM TargetMachine emit (dlopen C-API) when HAKO_CAPI_TM=1
|
|
int rc = -1;
|
|
#if !defined(_WIN32)
|
|
{
|
|
const char* tm = getenv("HAKO_CAPI_TM");
|
|
if (tm && tm[0]=='1') {
|
|
// Minimal dynamic loader for a subset of LLVM C-API
|
|
typedef void* (*p_LLVMContextCreate)(void);
|
|
typedef void (*p_LLVMContextDispose)(void*);
|
|
typedef void* (*p_LLVMCreateMemoryBufferWithContentsOfFile)(const char*, char**);
|
|
typedef void (*p_LLVMDisposeMemoryBuffer)(void*);
|
|
typedef int (*p_LLVMParseIRInContext)(void*, void*, void**, char**);
|
|
typedef char* (*p_LLVMGetDefaultTargetTriple)(void);
|
|
typedef void (*p_LLVMDisposeMessage)(char*);
|
|
typedef int (*p_LLVMGetTargetFromTriple)(const char*, void**, char**);
|
|
typedef void* (*p_LLVMCreateTargetMachine)(void*, const char*, const char*, const char*, int, int, int);
|
|
typedef int (*p_LLVMTargetMachineEmitToFile)(void*, void*, char*, int, char**);
|
|
typedef void (*p_LLVMDisposeTargetMachine)(void*);
|
|
typedef void (*p_LLVMDisposeModule)(void*);
|
|
typedef void* (*p_LLVMModuleCreateWithNameInContext)(const char*, void*);
|
|
// Target init (X86)
|
|
typedef void (*p_LLVMInitializeX86TargetInfo)(void);
|
|
typedef void (*p_LLVMInitializeX86Target)(void);
|
|
typedef void (*p_LLVMInitializeX86TargetMC)(void);
|
|
typedef void (*p_LLVMInitializeX86AsmPrinter)(void);
|
|
|
|
const char* cand[] = { "libLLVM-18.so", "libLLVM.so.18", "libLLVM.so", NULL };
|
|
void* h = NULL; for (int i=0;cand[i];i++){ h = dlopen(cand[i], RTLD_LAZY|RTLD_LOCAL); if (h) break; }
|
|
if (h) {
|
|
// Resolve required symbols
|
|
p_LLVMContextCreate f_LLVMContextCreate = (p_LLVMContextCreate)dlsym(h, "LLVMContextCreate");
|
|
p_LLVMContextDispose f_LLVMContextDispose = (p_LLVMContextDispose)dlsym(h, "LLVMContextDispose");
|
|
p_LLVMCreateMemoryBufferWithContentsOfFile f_LLVMCreateMemoryBufferWithContentsOfFile = (p_LLVMCreateMemoryBufferWithContentsOfFile)dlsym(h, "LLVMCreateMemoryBufferWithContentsOfFile");
|
|
p_LLVMDisposeMemoryBuffer f_LLVMDisposeMemoryBuffer = (p_LLVMDisposeMemoryBuffer)dlsym(h, "LLVMDisposeMemoryBuffer");
|
|
p_LLVMParseIRInContext f_LLVMParseIRInContext = (p_LLVMParseIRInContext)dlsym(h, "LLVMParseIRInContext");
|
|
p_LLVMGetDefaultTargetTriple f_LLVMGetDefaultTargetTriple = (p_LLVMGetDefaultTargetTriple)dlsym(h, "LLVMGetDefaultTargetTriple");
|
|
p_LLVMDisposeMessage f_LLVMDisposeMessage = (p_LLVMDisposeMessage)dlsym(h, "LLVMDisposeMessage");
|
|
p_LLVMGetTargetFromTriple f_LLVMGetTargetFromTriple = (p_LLVMGetTargetFromTriple)dlsym(h, "LLVMGetTargetFromTriple");
|
|
p_LLVMCreateTargetMachine f_LLVMCreateTargetMachine = (p_LLVMCreateTargetMachine)dlsym(h, "LLVMCreateTargetMachine");
|
|
p_LLVMTargetMachineEmitToFile f_LLVMTargetMachineEmitToFile = (p_LLVMTargetMachineEmitToFile)dlsym(h, "LLVMTargetMachineEmitToFile");
|
|
p_LLVMDisposeTargetMachine f_LLVMDisposeTargetMachine = (p_LLVMDisposeTargetMachine)dlsym(h, "LLVMDisposeTargetMachine");
|
|
p_LLVMDisposeModule f_LLVMDisposeModule = (p_LLVMDisposeModule)dlsym(h, "LLVMDisposeModule");
|
|
p_LLVMModuleCreateWithNameInContext f_LLVMModuleCreateWithNameInContext = (p_LLVMModuleCreateWithNameInContext)dlsym(h, "LLVMModuleCreateWithNameInContext");
|
|
// Target init
|
|
p_LLVMInitializeX86TargetInfo f_LLVMInitializeX86TargetInfo = (p_LLVMInitializeX86TargetInfo)dlsym(h, "LLVMInitializeX86TargetInfo");
|
|
p_LLVMInitializeX86Target f_LLVMInitializeX86Target = (p_LLVMInitializeX86Target)dlsym(h, "LLVMInitializeX86Target");
|
|
p_LLVMInitializeX86TargetMC f_LLVMInitializeX86TargetMC = (p_LLVMInitializeX86TargetMC)dlsym(h, "LLVMInitializeX86TargetMC");
|
|
p_LLVMInitializeX86AsmPrinter f_LLVMInitializeX86AsmPrinter = (p_LLVMInitializeX86AsmPrinter)dlsym(h, "LLVMInitializeX86AsmPrinter");
|
|
|
|
int ok = f_LLVMContextCreate && f_LLVMCreateMemoryBufferWithContentsOfFile && f_LLVMParseIRInContext &&
|
|
f_LLVMGetDefaultTargetTriple && f_LLVMGetTargetFromTriple && f_LLVMCreateTargetMachine &&
|
|
f_LLVMTargetMachineEmitToFile && f_LLVMDisposeTargetMachine && f_LLVMDisposeMessage &&
|
|
f_LLVMDisposeMemoryBuffer && f_LLVMContextDispose && f_LLVMDisposeModule &&
|
|
f_LLVMInitializeX86TargetInfo && f_LLVMInitializeX86Target && f_LLVMInitializeX86TargetMC && f_LLVMInitializeX86AsmPrinter;
|
|
if (ok) {
|
|
// Init targets
|
|
f_LLVMInitializeX86TargetInfo(); f_LLVMInitializeX86Target(); f_LLVMInitializeX86TargetMC(); f_LLVMInitializeX86AsmPrinter();
|
|
void* ctx = f_LLVMContextCreate();
|
|
char* emsg = NULL; void* buf = f_LLVMCreateMemoryBufferWithContentsOfFile(llpath, &emsg);
|
|
if (!buf) { if (emsg) f_LLVMDisposeMessage(emsg); goto TM_END; }
|
|
void* mod = NULL; if (f_LLVMParseIRInContext(ctx, buf, &mod, &emsg)) { if (emsg) f_LLVMDisposeMessage(emsg); f_LLVMDisposeMemoryBuffer(buf); goto TM_CTX; }
|
|
char* triple = f_LLVMGetDefaultTargetTriple();
|
|
void* tgt = NULL; if (f_LLVMGetTargetFromTriple(triple, &tgt, &emsg)) { if (emsg) f_LLVMDisposeMessage(emsg); goto TM_MOD; }
|
|
// Opt level
|
|
const char* ol = getenv("HAKO_LLVM_OPT_LEVEL"); if (!ol) ol = getenv("NYASH_LLVM_OPT_LEVEL"); if (!ol) ol = "0";
|
|
int opt = (ol[0]=='3')?3:(ol[0]=='2')?2:(ol[0]=='1')?1:0; // LLVMCodeGenOptLevel
|
|
void* tmachine = f_LLVMCreateTargetMachine(tgt, triple, "", "", opt, /*Reloc*/0, /*CodeModel*/0);
|
|
if (!tmachine) { goto TM_MOD; }
|
|
// Emit object (1 = LLVMObjectFile)
|
|
if (f_LLVMTargetMachineEmitToFile(tmachine, mod, (char*)obj_out, /*Object*/1, &emsg)) {
|
|
if (emsg) f_LLVMDisposeMessage(emsg);
|
|
} else {
|
|
rc = 0;
|
|
}
|
|
f_LLVMDisposeTargetMachine(tmachine);
|
|
TM_MOD:
|
|
if (mod) f_LLVMDisposeModule(mod);
|
|
if (triple) f_LLVMDisposeMessage(triple);
|
|
f_LLVMDisposeMemoryBuffer(buf);
|
|
TM_CTX:
|
|
f_LLVMContextDispose(ctx);
|
|
TM_END: ;
|
|
}
|
|
dlclose(h);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
if (rc != 0) {
|
|
char cmd[2048]; snprintf(cmd, sizeof(cmd), "llc -filetype=obj -o \"%s\" \"%s\" 2>/dev/null", obj_out, llpath);
|
|
rc = system(cmd);
|
|
}
|
|
remove(llpath);
|
|
yyjson_doc_free(d);
|
|
if (rc == 0) return 0;
|
|
GEN_ABORT:;
|
|
// fall through to pattern lowers
|
|
GEN_END: ;
|
|
} while(0);
|
|
// Try minimal pure path #1: recognize simple Ret(Const)
|
|
{
|
|
yyjson_read_err rerr0; yyjson_doc* d0 = yyjson_read_file(json_in, 0, NULL, &rerr0);
|
|
if (d0) {
|
|
yyjson_val* root0 = yyjson_doc_get_root(d0);
|
|
yyjson_val* fns0 = yyjson_obj_get(root0, "functions");
|
|
yyjson_val* fn00 = fns0 && yyjson_is_arr(fns0) ? yyjson_arr_get_first(fns0) : NULL;
|
|
yyjson_val* blocks0 = fn00 && yyjson_is_obj(fn00) ? yyjson_obj_get(fn00, "blocks") : NULL;
|
|
long long ret_const = 0; int have_ret = 0;
|
|
if (blocks0 && yyjson_is_arr(blocks0)) {
|
|
// Build dst->const map across blocks
|
|
// Note: i64 only for the minimal path
|
|
long long const_map_id[64]; long long const_map_val[64]; size_t const_n=0;
|
|
size_t blen0 = yyjson_arr_size(blocks0);
|
|
for (size_t bi=0; bi<blen0; bi++) {
|
|
yyjson_val* b = yyjson_arr_get(blocks0, bi);
|
|
yyjson_val* insts = yyjson_obj_get(b, "instructions"); if (!insts || !yyjson_is_arr(insts)) continue;
|
|
size_t ilen = yyjson_arr_size(insts);
|
|
for (size_t ii=0; ii<ilen; ii++) {
|
|
yyjson_val* ins = yyjson_arr_get(insts, ii);
|
|
const char* op = yyjson_get_str(yyjson_obj_get(ins, "op")); if (!op) continue;
|
|
if (strcmp(op, "const")==0) {
|
|
yyjson_val* dst = yyjson_obj_get(ins, "dst");
|
|
yyjson_val* vv = yyjson_obj_get(yyjson_obj_get(ins, "value"), "value");
|
|
if (dst && vv && const_n < 64) { const_map_id[const_n] = (long long)yyjson_get_sint(dst); const_map_val[const_n] = (long long)yyjson_get_sint(vv); const_n++; }
|
|
} else if (strcmp(op, "ret")==0) {
|
|
yyjson_val* v = yyjson_obj_get(ins, "value");
|
|
if (v) {
|
|
long long vid = (long long)yyjson_get_sint(v);
|
|
for (size_t k=0;k<const_n;k++){ if (const_map_id[k]==vid){ ret_const = const_map_val[k]; have_ret=1; break; } }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (have_ret) {
|
|
char llpath[1024]; snprintf(llpath, sizeof(llpath), "%s/hako_pure_ret_%d.ll", "/tmp", (int)getpid());
|
|
FILE* f = fopen(llpath, "wb"); if (!f) { yyjson_doc_free(d0); return set_err_owned(err_out, "failed to open .ll"); }
|
|
fprintf(f, "; nyash minimal pure IR (ret const)\n");
|
|
fprintf(f, "target triple = \"x86_64-pc-linux-gnu\"\n\n");
|
|
fprintf(f, "define i64 @ny_main() {\n ret i64 %lld\n}\n", ret_const);
|
|
fclose(f);
|
|
char cmd[2048]; snprintf(cmd, sizeof(cmd), "llc -filetype=obj -o \"%s\" \"%s\" 2>/dev/null", obj_out, llpath);
|
|
int rc = system(cmd); remove(llpath); yyjson_doc_free(d0);
|
|
if (rc == 0) return 0;
|
|
// else continue to try pattern #2 or fallback
|
|
} else {
|
|
yyjson_doc_free(d0);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try minimal pure path #2: recognize simple If (const/compare/branch + two const blocks + merge ret)
|
|
// Parse JSON quickly via yyjson and synthesize IR when possible.
|
|
yyjson_read_err rerr; yyjson_doc* doc = yyjson_read_file(json_in, 0, NULL, &rerr);
|
|
if (!doc) {
|
|
return set_err_owned(err_out, "json read failed");
|
|
}
|
|
yyjson_val* root = yyjson_doc_get_root(doc);
|
|
yyjson_val* fns = yyjson_obj_get(root, "functions");
|
|
yyjson_val* fn0 = fns && yyjson_is_arr(fns) ? yyjson_arr_get_first(fns) : NULL;
|
|
yyjson_val* blocks = fn0 && yyjson_is_obj(fn0) ? yyjson_obj_get(fn0, "blocks") : NULL;
|
|
if (!(blocks && yyjson_is_arr(blocks) && yyjson_arr_size(blocks) >= 3)) {
|
|
yyjson_doc_free(doc);
|
|
return hako_aot_compile_json(json_in, obj_out, err_out);
|
|
}
|
|
// Expect block0: const a, const b, compare Lt, branch then=t else=e
|
|
yyjson_val* b0 = yyjson_arr_get_first(blocks);
|
|
yyjson_val* i0 = b0 ? yyjson_obj_get(b0, "instructions") : NULL;
|
|
if (!(i0 && yyjson_is_arr(i0) && yyjson_arr_size(i0) >= 4)) {
|
|
yyjson_doc_free(doc);
|
|
return hako_aot_compile_json(json_in, obj_out, err_out);
|
|
}
|
|
// const #1
|
|
yyjson_val* ins0 = yyjson_arr_get(i0, 0);
|
|
yyjson_val* ins1 = yyjson_arr_get(i0, 1);
|
|
yyjson_val* ins2 = yyjson_arr_get(i0, 2);
|
|
yyjson_val* ins3 = yyjson_arr_get(i0, 3);
|
|
const char *op0 = yyjson_get_str(yyjson_obj_get(ins0, "op"));
|
|
const char *op1 = yyjson_get_str(yyjson_obj_get(ins1, "op"));
|
|
const char *op2 = yyjson_get_str(yyjson_obj_get(ins2, "op"));
|
|
const char *op3 = yyjson_get_str(yyjson_obj_get(ins3, "op"));
|
|
if (!(op0 && op1 && op2 && op3 && strcmp(op0,"const")==0 && strcmp(op1,"const")==0 && strcmp(op2,"compare")==0 && strcmp(op3,"branch")==0)) {
|
|
yyjson_doc_free(doc);
|
|
return hako_aot_compile_json(json_in, obj_out, err_out);
|
|
}
|
|
// Read const values and branch targets
|
|
yyjson_val* v0 = yyjson_obj_get(yyjson_obj_get(ins0, "value"), "value");
|
|
yyjson_val* v1 = yyjson_obj_get(yyjson_obj_get(ins1, "value"), "value");
|
|
long long c0 = v0 ? (long long)yyjson_get_sint(v0) : 0;
|
|
long long c1 = v1 ? (long long)yyjson_get_sint(v1) : 0;
|
|
const char* cmp = yyjson_get_str(yyjson_obj_get(ins2, "cmp"));
|
|
const char* pred = NULL;
|
|
if (!cmp) { yyjson_doc_free(doc); return hako_aot_compile_json(json_in, obj_out, err_out); }
|
|
if (strcmp(cmp,"Lt")==0 || strcmp(cmp,"lt")==0) pred = "slt";
|
|
else if (strcmp(cmp,"Le")==0 || strcmp(cmp,"LE")==0 || strcmp(cmp,"le")==0) pred = "sle";
|
|
else if (strcmp(cmp,"Eq")==0 || strcmp(cmp,"eq")==0) pred = "eq";
|
|
else if (strcmp(cmp,"Ne")==0 || strcmp(cmp,"ne")==0) pred = "ne";
|
|
else if (strcmp(cmp,"Ge")==0 || strcmp(cmp,"ge")==0) pred = "sge";
|
|
else if (strcmp(cmp,"Gt")==0 || strcmp(cmp,"gt")==0) pred = "sgt";
|
|
else { yyjson_doc_free(doc); return hako_aot_compile_json(json_in, obj_out, err_out); }
|
|
int then_id = (int)yyjson_get_sint(yyjson_obj_get(ins3, "then"));
|
|
int else_id = (int)yyjson_get_sint(yyjson_obj_get(ins3, "else"));
|
|
// Fetch then/else blocks and merge ret block
|
|
yyjson_val* b_then = NULL; yyjson_val* b_else = NULL; yyjson_val* b_merge = NULL;
|
|
size_t blen = yyjson_arr_size(blocks);
|
|
for (size_t i=0;i<blen;i++) {
|
|
yyjson_val* bi = yyjson_arr_get(blocks, i);
|
|
int bid = (int)yyjson_get_sint(yyjson_obj_get(bi, "id"));
|
|
if (bid == then_id) b_then = bi; else if (bid == else_id) b_else = bi;
|
|
}
|
|
// Merge is any block that has a ret; find it
|
|
for (size_t i=0;i<blen;i++) {
|
|
yyjson_val* bi = yyjson_arr_get(blocks, i);
|
|
yyjson_val* insts = yyjson_obj_get(bi, "instructions");
|
|
size_t ilen = insts && yyjson_is_arr(insts) ? yyjson_arr_size(insts) : 0;
|
|
for (size_t k=0;k<ilen;k++) {
|
|
yyjson_val* ins = yyjson_arr_get(insts, k);
|
|
const char* op = yyjson_get_str(yyjson_obj_get(ins, "op"));
|
|
if (op && strcmp(op, "ret")==0) { b_merge = bi; break; }
|
|
}
|
|
if (b_merge) break;
|
|
}
|
|
if (!(b_then && b_else && b_merge)) { yyjson_doc_free(doc); return hako_aot_compile_json(json_in, obj_out, err_out); }
|
|
// Extract constants in then/else blocks
|
|
yyjson_val* it = yyjson_obj_get(b_then, "instructions");
|
|
yyjson_val* ie = yyjson_obj_get(b_else, "instructions");
|
|
if (!(it && ie)) { yyjson_doc_free(doc); return hako_aot_compile_json(json_in, obj_out, err_out); }
|
|
yyjson_val* t0 = yyjson_arr_get(it, 0);
|
|
yyjson_val* e0 = yyjson_arr_get(ie, 0);
|
|
if (!(t0 && e0)) { yyjson_doc_free(doc); return hako_aot_compile_json(json_in, obj_out, err_out); }
|
|
yyjson_val* tv = yyjson_obj_get(yyjson_obj_get(t0, "value"), "value");
|
|
yyjson_val* ev = yyjson_obj_get(yyjson_obj_get(e0, "value"), "value");
|
|
long long then_const = tv ? (long long)yyjson_get_sint(tv) : 0;
|
|
long long else_const = ev ? (long long)yyjson_get_sint(ev) : 0;
|
|
// Synthesize minimal IR
|
|
char llpath[1024]; snprintf(llpath, sizeof(llpath), "%s/hako_pure_%d.ll", "/tmp", (int)getpid());
|
|
FILE* f = fopen(llpath, "wb");
|
|
if (!f) { yyjson_doc_free(doc); return set_err_owned(err_out, "failed to open .ll"); }
|
|
fprintf(f, "; nyash minimal pure IR\n");
|
|
fprintf(f, "target triple = \"x86_64-pc-linux-gnu\"\n\n");
|
|
fprintf(f, "define i64 @ny_main() {\n");
|
|
fprintf(f, "bb0:\n");
|
|
fprintf(f, " %%cmp = icmp %s i64 %lld, %lld\n", pred, c0, c1);
|
|
fprintf(f, " br i1 %%cmp, label %%bb_then, label %%bb_else\n\n");
|
|
fprintf(f, "bb_then:\n br label %%bb_merge\n\n");
|
|
fprintf(f, "bb_else:\n br label %%bb_merge\n\n");
|
|
fprintf(f, "bb_merge:\n %%r = phi i64 [ %lld, %%bb_then ], [ %lld, %%bb_else ]\n ret i64 %%r\n}\n", then_const, else_const);
|
|
fclose(f);
|
|
// Run llc to emit object
|
|
char cmd[2048]; snprintf(cmd, sizeof(cmd), "llc -filetype=obj -o \"%s\" \"%s\" 2>/dev/null", obj_out, llpath);
|
|
int rc = system(cmd);
|
|
remove(llpath);
|
|
yyjson_doc_free(doc);
|
|
if (rc != 0) {
|
|
// Fallback for environments without llc
|
|
return hako_aot_compile_json(json_in, obj_out, err_out);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// Try minimal pure path #3: Map birth → set → size → ret
|
|
{
|
|
yyjson_read_err rerr; yyjson_doc* doc = yyjson_read_file(json_in, 0, NULL, &rerr);
|
|
if (doc) {
|
|
yyjson_val* root = yyjson_doc_get_root(doc);
|
|
yyjson_val* fns = yyjson_obj_get(root, "functions");
|
|
yyjson_val* fn0 = fns && yyjson_is_arr(fns) ? yyjson_arr_get_first(fns) : NULL;
|
|
yyjson_val* blocks = fn0 && yyjson_is_obj(fn0) ? yyjson_obj_get(fn0, "blocks") : NULL;
|
|
yyjson_val* b0 = blocks && yyjson_is_arr(blocks) ? yyjson_arr_get_first(blocks) : NULL;
|
|
yyjson_val* insts = b0 ? yyjson_obj_get(b0, "instructions") : NULL;
|
|
long long key_c = 0, val_c = 0; int have = 0;
|
|
if (insts && yyjson_is_arr(insts) && yyjson_arr_size(insts) >= 5) {
|
|
// const, const, mir_call(Constructor:MapBox), mir_call(Method:set), mir_call(Method:size), ret
|
|
yyjson_val* i0 = yyjson_arr_get(insts, 0);
|
|
yyjson_val* i1 = yyjson_arr_get(insts, 1);
|
|
yyjson_val* i2 = yyjson_arr_get(insts, 2);
|
|
yyjson_val* i3 = yyjson_arr_get(insts, 3);
|
|
yyjson_val* i4 = yyjson_arr_get(insts, 4);
|
|
const char* op0 = yyjson_get_str(yyjson_obj_get(i0, "op"));
|
|
const char* op1 = yyjson_get_str(yyjson_obj_get(i1, "op"));
|
|
const char* op2 = yyjson_get_str(yyjson_obj_get(i2, "op"));
|
|
const char* op3 = yyjson_get_str(yyjson_obj_get(i3, "op"));
|
|
const char* op4 = yyjson_get_str(yyjson_obj_get(i4, "op"));
|
|
if (op0 && op1 && op2 && op3 && op4 &&
|
|
strcmp(op0, "const")==0 && strcmp(op1, "const")==0 &&
|
|
strcmp(op2, "mir_call")==0 && strcmp(op3, "mir_call")==0 && strcmp(op4, "mir_call")==0) {
|
|
yyjson_val* v0 = yyjson_obj_get(yyjson_obj_get(i0, "value"), "value");
|
|
yyjson_val* v1 = yyjson_obj_get(yyjson_obj_get(i1, "value"), "value");
|
|
key_c = v0 ? (long long)yyjson_get_sint(v0) : 0;
|
|
val_c = v1 ? (long long)yyjson_get_sint(v1) : 0;
|
|
// Constructor MapBox
|
|
yyjson_val* mc2 = yyjson_obj_get(i2, "mir_call");
|
|
yyjson_val* cal2 = mc2 ? yyjson_obj_get(mc2, "callee") : NULL;
|
|
const char* ctype = cal2 ? yyjson_get_str(yyjson_obj_get(cal2, "type")) : NULL;
|
|
const char* cname = cal2 ? yyjson_get_str(yyjson_obj_get(cal2, "name")) : NULL;
|
|
// Method set/size
|
|
yyjson_val* mc3 = yyjson_obj_get(i3, "mir_call");
|
|
yyjson_val* cal3 = mc3 ? yyjson_obj_get(mc3, "callee") : NULL;
|
|
const char* m3 = cal3 ? yyjson_get_str(yyjson_obj_get(cal3, "name")) : NULL;
|
|
yyjson_val* mc4 = yyjson_obj_get(i4, "mir_call");
|
|
yyjson_val* cal4 = mc4 ? yyjson_obj_get(mc4, "callee") : NULL;
|
|
const char* m4 = cal4 ? yyjson_get_str(yyjson_obj_get(cal4, "name")) : NULL;
|
|
if (ctype && cname && strcmp(ctype, "Constructor")==0 && strcmp(cname, "MapBox")==0 &&
|
|
m3 && strcmp(m3, "set")==0 && m4 && (strcmp(m4, "size")==0 || strcmp(m4, "len")==0)) {
|
|
have = 1;
|
|
}
|
|
}
|
|
}
|
|
if (have) {
|
|
char llpath[1024]; snprintf(llpath, sizeof(llpath), "%s/hako_pure_map_%d.ll", "/tmp", (int)getpid());
|
|
FILE* f = fopen(llpath, "wb"); if (!f) { yyjson_doc_free(doc); return set_err_owned(err_out, "failed to open .ll"); }
|
|
fprintf(f, "; nyash minimal pure IR (map set->size)\n");
|
|
fprintf(f, "target triple = \"x86_64-pc-linux-gnu\"\n\n");
|
|
fprintf(f, "declare i64 @\"nyash.map.birth_h\"()\n");
|
|
fprintf(f, "declare i64 @\"nyash.map.set_h\"(i64, i64, i64)\n");
|
|
fprintf(f, "declare i64 @\"nyash.map.size_h\"(i64)\n\n");
|
|
fprintf(f, "define i64 @ny_main() {\n");
|
|
fprintf(f, " %%h = call i64 @\"nyash.map.birth_h\"()\n");
|
|
fprintf(f, " %%_s = call i64 @\"nyash.map.set_h\"(i64 %%h, i64 %lld, i64 %lld)\n", key_c, val_c);
|
|
fprintf(f, " %%sz = call i64 @\"nyash.map.size_h\"(i64 %%h)\n");
|
|
fprintf(f, " ret i64 %%sz\n}\n");
|
|
fclose(f);
|
|
char cmd[2048]; snprintf(cmd, sizeof(cmd), "llc -filetype=obj -o \"%s\" \"%s\" 2>/dev/null", obj_out, llpath);
|
|
int rc = system(cmd); remove(llpath); yyjson_doc_free(doc);
|
|
if (rc == 0) return 0;
|
|
} else {
|
|
yyjson_doc_free(doc);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try minimal pure path #4: Array birth → push → len → ret
|
|
{
|
|
yyjson_read_err rerr; yyjson_doc* doc = yyjson_read_file(json_in, 0, NULL, &rerr);
|
|
if (doc) {
|
|
yyjson_val* root = yyjson_doc_get_root(doc);
|
|
yyjson_val* fns = yyjson_obj_get(root, "functions");
|
|
yyjson_val* fn0 = fns && yyjson_is_arr(fns) ? yyjson_arr_get_first(fns) : NULL;
|
|
yyjson_val* blocks = fn0 && yyjson_is_obj(fn0) ? yyjson_obj_get(fn0, "blocks") : NULL;
|
|
yyjson_val* b0 = blocks && yyjson_is_arr(blocks) ? yyjson_arr_get_first(blocks) : NULL;
|
|
yyjson_val* insts = b0 ? yyjson_obj_get(b0, "instructions") : NULL;
|
|
long long val_c = 0; int have = 0;
|
|
if (insts && yyjson_is_arr(insts) && yyjson_arr_size(insts) >= 4) {
|
|
// const, mir_call(Constructor:ArrayBox), mir_call(Method:push), mir_call(Method:len/length/size), ret
|
|
yyjson_val* i0 = yyjson_arr_get(insts, 0);
|
|
yyjson_val* i1 = yyjson_arr_get(insts, 1);
|
|
yyjson_val* i2 = yyjson_arr_get(insts, 2);
|
|
yyjson_val* i3 = yyjson_arr_get(insts, 3);
|
|
const char* op0 = yyjson_get_str(yyjson_obj_get(i0, "op"));
|
|
const char* op1 = yyjson_get_str(yyjson_obj_get(i1, "op"));
|
|
const char* op2 = yyjson_get_str(yyjson_obj_get(i2, "op"));
|
|
const char* op3 = yyjson_get_str(yyjson_obj_get(i3, "op"));
|
|
if (op0 && op1 && op2 && op3 && strcmp(op0, "const")==0 && strcmp(op1, "mir_call")==0 && strcmp(op2, "mir_call")==0 && strcmp(op3, "mir_call")==0) {
|
|
yyjson_val* v0 = yyjson_obj_get(yyjson_obj_get(i0, "value"), "value");
|
|
val_c = v0 ? (long long)yyjson_get_sint(v0) : 0;
|
|
// Constructor ArrayBox
|
|
yyjson_val* mc1 = yyjson_obj_get(i1, "mir_call");
|
|
yyjson_val* cal1 = mc1 ? yyjson_obj_get(mc1, "callee") : NULL;
|
|
const char* ctype = cal1 ? yyjson_get_str(yyjson_obj_get(cal1, "type")) : NULL;
|
|
const char* cname = cal1 ? yyjson_get_str(yyjson_obj_get(cal1, "name")) : NULL;
|
|
// Method push/len
|
|
yyjson_val* mc2 = yyjson_obj_get(i2, "mir_call");
|
|
yyjson_val* cal2 = mc2 ? yyjson_obj_get(mc2, "callee") : NULL;
|
|
const char* m2 = cal2 ? yyjson_get_str(yyjson_obj_get(cal2, "name")) : NULL;
|
|
yyjson_val* mc3 = yyjson_obj_get(i3, "mir_call");
|
|
yyjson_val* cal3 = mc3 ? yyjson_obj_get(mc3, "callee") : NULL;
|
|
const char* m3 = cal3 ? yyjson_get_str(yyjson_obj_get(cal3, "name")) : NULL;
|
|
if (ctype && cname && strcmp(ctype, "Constructor")==0 && strcmp(cname, "ArrayBox")==0 &&
|
|
m2 && strcmp(m2, "push")==0 && m3 && (strcmp(m3, "len")==0 || strcmp(m3, "length")==0 || strcmp(m3, "size")==0)) {
|
|
have = 1;
|
|
}
|
|
}
|
|
}
|
|
if (have) {
|
|
char llpath[1024]; snprintf(llpath, sizeof(llpath), "%s/hako_pure_array_%d.ll", "/tmp", (int)getpid());
|
|
FILE* f = fopen(llpath, "wb"); if (!f) { yyjson_doc_free(doc); return set_err_owned(err_out, "failed to open .ll"); }
|
|
fprintf(f, "; nyash minimal pure IR (array push->len)\n");
|
|
fprintf(f, "target triple = \"x86_64-pc-linux-gnu\"\n\n");
|
|
fprintf(f, "declare i64 @\"nyash.array.birth_h\"()\n");
|
|
fprintf(f, "declare i64 @\"nyash.array.push_h\"(i64, i64)\n");
|
|
fprintf(f, "declare i64 @\"nyash.array.len_h\"(i64)\n\n");
|
|
fprintf(f, "define i64 @ny_main() {\n");
|
|
fprintf(f, " %%h = call i64 @\"nyash.array.birth_h\"()\n");
|
|
fprintf(f, " %%_p = call i64 @\"nyash.array.push_h\"(i64 %%h, i64 %lld)\n", val_c);
|
|
fprintf(f, " %%len = call i64 @\"nyash.array.len_h\"(i64 %%h)\n");
|
|
fprintf(f, " ret i64 %%len\n}\n");
|
|
fclose(f);
|
|
char cmd[2048]; snprintf(cmd, sizeof(cmd), "llc -filetype=obj -o \"%s\" \"%s\" 2>/dev/null", obj_out, llpath);
|
|
int rc = system(cmd); remove(llpath); yyjson_doc_free(doc);
|
|
if (rc == 0) return 0;
|
|
} else {
|
|
yyjson_doc_free(doc);
|
|
}
|
|
}
|
|
}
|
|
return hako_aot_compile_json(json_in, obj_out, err_out);
|
|
}
|
|
|
|
__attribute__((visibility("default")))
|
|
int hako_llvmc_link_obj(const char* obj_in, const char* exe_out, const char* extra_ldflags, char** err_out) {
|
|
if (capi_pure_enabled()) {
|
|
// Phase 21.2 (step-1): route to existing AOT helper (linker) first.
|
|
return hako_aot_link_obj(obj_in, exe_out, extra_ldflags, err_out);
|
|
}
|
|
return hako_aot_link_obj(obj_in, exe_out, extra_ldflags, err_out);
|
|
}
|