mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-27 06:43:19 +08:00
Cache each constant method name's id in a slot
A checked-dynamic method call looked its name up in the method table on every call: a strlen, a hash and a byte comparison of the name. The compiler now gives each method-name constant an i32 slot that starts at -1 and passes it with the call (scr_dyn_invoke_prepared_id, and the Buffer method entries, which forward it). The first call stores the name's id in the slot, and later calls read it. Ids stay the runtime's own, so the slot holds whatever this runtime's table says. Names are still matched by content on the first call, and the slot is written with a relaxed atomic store, since every thread writes the same id. The runtime ABI is now 28.
This commit is contained in:
@@ -72,7 +72,7 @@ describe.runIf(supported)("macOS arm64 native link info", () => {
|
||||
minimum_os: "14.0",
|
||||
},
|
||||
program: { object, entry_symbol: "main" },
|
||||
runtime_abi: { version: 27, marker: "scr_runtime_abi_v27" },
|
||||
runtime_abi: { version: 28, marker: "scr_runtime_abi_v28" },
|
||||
runtime_pack: {
|
||||
kind: "precompiled",
|
||||
package: "@scriptc/runtime-darwin-arm64",
|
||||
@@ -165,6 +165,6 @@ describe.runIf(supported)("macOS arm64 native link info", () => {
|
||||
() => null,
|
||||
(failure: { stderr?: string }) => failure,
|
||||
);
|
||||
expect(error?.stderr).toContain("scr_runtime_abi_v27");
|
||||
expect(error?.stderr).toContain("scr_runtime_abi_v28");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -727,6 +727,8 @@ export function emitModuleParts(emitter: LlEmitter): string[] {
|
||||
out.push(`@${sym} = internal constant { ptr, ptr, ptr, ptr, ptr, ptr, ptr } { ${body} }`);
|
||||
}
|
||||
if (emitter.mapOpsConsts.size > 0) out.push(``);
|
||||
for (const sym of emitter.methodIdSlots.values()) out.push(`@${sym} = internal global i32 -1`);
|
||||
if (emitter.methodIdSlots.size > 0) out.push(``);
|
||||
|
||||
for (const line of ffiCallbacks.globals) out.push(line);
|
||||
if (ffiCallbacks.globals.length > 0) out.push(``);
|
||||
|
||||
@@ -782,6 +782,10 @@ export class LlEmitter {
|
||||
readonly cstrs = new Map<string, { sym: string; len: number }>();
|
||||
/** Interned ScrMapOps descriptors (scr_map.h): initializer → symbol. */
|
||||
readonly mapOpsConsts = new Map<string, string>();
|
||||
/** Method-id slots of checked-dynamic method calls
|
||||
* (scr_dyn_invoke_prepared_id): method name → symbol of an i32 the
|
||||
* runtime fills with the name's id on the first call. */
|
||||
readonly methodIdSlots = new Map<string, string>();
|
||||
/** Type-directed walker functions (JSON serializers, the indent
|
||||
* rewriter, union ToString/join) — interned per typeKey/unionId, defs
|
||||
* flushed with the shape helpers. */
|
||||
@@ -2400,6 +2404,16 @@ export class LlEmitter {
|
||||
return `@${c.sym}`;
|
||||
}
|
||||
|
||||
/** The method-id slot for a constant method name (methodIdSlots). */
|
||||
methodIdSlot(name: string): string {
|
||||
let sym = this.methodIdSlots.get(name);
|
||||
if (!sym) {
|
||||
sym = this.contentSymbol("sc_mid_", name);
|
||||
this.methodIdSlots.set(name, sym);
|
||||
}
|
||||
return `@${sym}`;
|
||||
}
|
||||
|
||||
needBadTag(): void {
|
||||
this.needsBadTag = true;
|
||||
}
|
||||
|
||||
@@ -250,8 +250,10 @@ export function emitDynamicExpr(
|
||||
}
|
||||
// Buffer.prototype method names take their scr_bytes_dyn.c entry, which
|
||||
// runs them on Buffer receivers and dispatches any other receiver.
|
||||
const invoke = DYN_BUFFER_METHODS.get(e.method) ?? "scr_dyn_invoke_prepared";
|
||||
host.declare(`declare ptr @${invoke}(ptr, ptr, ptr, ptr, ${host.sizeType}, ptr)`);
|
||||
// Each call passes its method name's id slot, so only the first call
|
||||
// looks the name up.
|
||||
const invoke = DYN_BUFFER_METHODS.get(e.method) ?? "scr_dyn_invoke_prepared_id";
|
||||
host.declare(`declare ptr @${invoke}(ptr, ptr, ptr, ptr, ptr, ${host.sizeType}, ptr)`);
|
||||
let name = host.cstr(e.calleeName);
|
||||
if (e.calleeNameValue !== undefined) {
|
||||
const value = emitBorrowedInput(host, e.calleeNameValue);
|
||||
@@ -260,7 +262,7 @@ export function emitDynamicExpr(
|
||||
}
|
||||
const t = B.tmp();
|
||||
B.line(
|
||||
`${t} = call ptr @${invoke}(ptr ${recv.name}, ptr ${prepared}, ptr ${host.cstr(e.method)}, ptr ${argsPtr}, ${host.sizeType} ${args.length}, ptr ${name})`,
|
||||
`${t} = call ptr @${invoke}(ptr ${recv.name}, ptr ${prepared}, ptr ${host.cstr(e.method)}, ptr ${host.methodIdSlot(e.method)}, ptr ${argsPtr}, ${host.sizeType} ${args.length}, ptr ${name})`,
|
||||
);
|
||||
const out = host.own({ name: t, type: e.type });
|
||||
host.emitPendingCheck();
|
||||
|
||||
@@ -732,7 +732,7 @@ function emitKeyed(host: Host, e: ExprOf<"call">, callee: IrFunction): LlValue {
|
||||
|
||||
/** dynInvoke's method read on an ordinary object through the site's
|
||||
* cache, else scr_dyn_prepare_method. Returns the owned method (or NULL,
|
||||
* which scr_dyn_invoke_prepared answers with the full dispatch). */
|
||||
* which scr_dyn_invoke_prepared_id answers with the full dispatch). */
|
||||
export function emitJsIcMethod(host: Host, e: ExprOf<"dynInvoke">, receiver: string): string {
|
||||
const B = host.B;
|
||||
const method = e.method;
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
* of the link contract: a program object keeps this symbol undefined and the
|
||||
* matching runtime defines it, so incompatible manual links fail before the
|
||||
* program can start. */
|
||||
export const RUNTIME_ABI_VERSION = 27 as const;
|
||||
export const RUNTIME_ABI_MARKER = "scr_runtime_abi_v27" as const;
|
||||
export const RUNTIME_ABI_VERSION = 28 as const;
|
||||
export const RUNTIME_ABI_MARKER = "scr_runtime_abi_v28" as const;
|
||||
|
||||
/** Object consumption is useful today, but the complete scr_* surface may
|
||||
* still change before 1.0. The versioned marker prevents accidental mixing;
|
||||
|
||||
@@ -52,7 +52,7 @@ async function fixture(target: NativeTargetSpec, workers = false) {
|
||||
object_format: target.objectFormat,
|
||||
minimum_os: target.minimumOs,
|
||||
},
|
||||
runtime_abi: { version: 27, marker: "scr_runtime_abi_v27" },
|
||||
runtime_abi: { version: 28, marker: "scr_runtime_abi_v28" },
|
||||
compiler: { command: "fixture", identity: "fixture", target: target.llvmTriple },
|
||||
macros: { executable: [], excluded: ["SCR_LIB"], sanitizer: "external-toolchain-required" },
|
||||
flavors: {
|
||||
|
||||
@@ -127,7 +127,7 @@ async function fixture(target: NativeTargetSpec = MACOS_ARM64_TARGET) {
|
||||
object_format: target.objectFormat,
|
||||
minimum_os: target.minimumOs,
|
||||
},
|
||||
runtime_abi: { version: 27, marker: "scr_runtime_abi_v27" },
|
||||
runtime_abi: { version: 28, marker: "scr_runtime_abi_v28" },
|
||||
compiler: {
|
||||
command: "clang",
|
||||
identity: "fixture clang",
|
||||
|
||||
@@ -378,7 +378,7 @@ done:
|
||||
expect(await symbols(helperObject, ["-u"])).toEqual(await symbols(clangObject, ["-u"]));
|
||||
expect(await symbols(helperObject, ["-gU"])).toEqual(await symbols(clangObject, ["-gU"]));
|
||||
expect(await symbols(helperObject, ["-gU"])).toEqual(["0000000000000000 T _main"]);
|
||||
expect(await symbols(helperObject, ["-u"])).toContain("_scr_runtime_abi_v27");
|
||||
expect(await symbols(helperObject, ["-u"])).toContain("_scr_runtime_abi_v28");
|
||||
|
||||
const clangExe = join(dir, "clang-program");
|
||||
const linkDriver = join(dir, "clang-driver.c");
|
||||
@@ -712,7 +712,7 @@ done:
|
||||
.split("\n");
|
||||
expect(undefinedSymbols).toContain("_sf_scale");
|
||||
expect(undefinedSymbols).toContain("_sf_callback_mix");
|
||||
expect(undefinedSymbols).toContain("_scr_runtime_abi_v27");
|
||||
expect(undefinedSymbols).toContain("_scr_runtime_abi_v28");
|
||||
});
|
||||
|
||||
test("helper and clang object paths retain the same outbound FFI C ABI", async () => {
|
||||
|
||||
@@ -340,7 +340,7 @@ async function build() {
|
||||
package: packageManifest.name,
|
||||
version: packageManifest.version,
|
||||
target: RUNTIME_PACK_MATRIX.target,
|
||||
runtime_abi: { version: 27, marker: "scr_runtime_abi_v27" },
|
||||
runtime_abi: { version: 28, marker: "scr_runtime_abi_v28" },
|
||||
compiler: {
|
||||
command: compiler,
|
||||
identity: compilerVersion,
|
||||
|
||||
@@ -437,7 +437,7 @@ async function build() {
|
||||
package: packageManifest.name,
|
||||
version: packageManifest.version,
|
||||
target: matrix.target,
|
||||
runtime_abi: { version: 27, marker: "scr_runtime_abi_v27" },
|
||||
runtime_abi: { version: 28, marker: "scr_runtime_abi_v28" },
|
||||
compiler: {
|
||||
command: compiler,
|
||||
identity: compilerVersion,
|
||||
|
||||
@@ -509,8 +509,9 @@ static bool scr_bytes_dyn_buffer(const ScrDyn *recv) {
|
||||
/* One entry per method family, so a program links only the families it
|
||||
* names: each runs its methods on a Buffer receiver and gives any other
|
||||
* receiver the ordinary dispatch. */
|
||||
ScrDyn *scr_dyn_invoke_buffer_num(ScrDyn *recv, ScrDyn *callee, const char *method, ScrDyn *const *args,
|
||||
size_t argc, const char *what) {
|
||||
ScrDyn *scr_dyn_invoke_buffer_num(ScrDyn *recv, ScrDyn *callee, const char *method,
|
||||
int32_t *mid_slot, ScrDyn *const *args, size_t argc,
|
||||
const char *what) {
|
||||
if (scr_bytes_dyn_buffer(recv)) {
|
||||
bool read = !strncmp(method, "read", 4);
|
||||
const char *rest = method + (read ? 4 : 5);
|
||||
@@ -518,40 +519,45 @@ ScrDyn *scr_dyn_invoke_buffer_num(ScrDyn *recv, ScrDyn *callee, const char *meth
|
||||
if (!strcmp(rest, scr_bytes_dyn_names[at].name)) return scr_bytes_dyn_num_method(recv->v.bytes, read, at, args, argc);
|
||||
}
|
||||
}
|
||||
return scr_dyn_invoke_prepared(recv, callee, method, args, argc, what);
|
||||
return scr_dyn_invoke_prepared_id(recv, callee, method, mid_slot, args, argc, what);
|
||||
}
|
||||
|
||||
ScrDyn *scr_dyn_invoke_buffer_write(ScrDyn *recv, ScrDyn *callee, const char *method, ScrDyn *const *args,
|
||||
size_t argc, const char *what) {
|
||||
ScrDyn *scr_dyn_invoke_buffer_write(ScrDyn *recv, ScrDyn *callee, const char *method,
|
||||
int32_t *mid_slot, ScrDyn *const *args, size_t argc,
|
||||
const char *what) {
|
||||
if (scr_bytes_dyn_buffer(recv)) return scr_bytes_dyn_write(recv->v.bytes, args, argc);
|
||||
return scr_dyn_invoke_prepared(recv, callee, method, args, argc, what);
|
||||
return scr_dyn_invoke_prepared_id(recv, callee, method, mid_slot, args, argc, what);
|
||||
}
|
||||
|
||||
ScrDyn *scr_dyn_invoke_buffer_fill(ScrDyn *recv, ScrDyn *callee, const char *method, ScrDyn *const *args,
|
||||
size_t argc, const char *what) {
|
||||
ScrDyn *scr_dyn_invoke_buffer_fill(ScrDyn *recv, ScrDyn *callee, const char *method,
|
||||
int32_t *mid_slot, ScrDyn *const *args, size_t argc,
|
||||
const char *what) {
|
||||
if (scr_bytes_dyn_buffer(recv)) return scr_bytes_dyn_fill(recv, args, argc);
|
||||
return scr_dyn_invoke_prepared(recv, callee, method, args, argc, what);
|
||||
return scr_dyn_invoke_prepared_id(recv, callee, method, mid_slot, args, argc, what);
|
||||
}
|
||||
|
||||
ScrDyn *scr_dyn_invoke_buffer_to_string(ScrDyn *recv, ScrDyn *callee, const char *method, ScrDyn *const *args,
|
||||
size_t argc, const char *what) {
|
||||
ScrDyn *scr_dyn_invoke_buffer_to_string(ScrDyn *recv, ScrDyn *callee, const char *method,
|
||||
int32_t *mid_slot, ScrDyn *const *args, size_t argc,
|
||||
const char *what) {
|
||||
if (scr_bytes_dyn_buffer(recv)) return scr_bytes_dyn_to_string(recv->v.bytes, args, argc);
|
||||
return scr_dyn_invoke_prepared(recv, callee, method, args, argc, what);
|
||||
return scr_dyn_invoke_prepared_id(recv, callee, method, mid_slot, args, argc, what);
|
||||
}
|
||||
|
||||
/* indexOf, lastIndexOf and includes. */
|
||||
ScrDyn *scr_dyn_invoke_buffer_search(ScrDyn *recv, ScrDyn *callee, const char *method, ScrDyn *const *args,
|
||||
size_t argc, const char *what) {
|
||||
ScrDyn *scr_dyn_invoke_buffer_search(ScrDyn *recv, ScrDyn *callee, const char *method,
|
||||
int32_t *mid_slot, ScrDyn *const *args, size_t argc,
|
||||
const char *what) {
|
||||
if (scr_bytes_dyn_buffer(recv)) {
|
||||
bool includes = !strcmp(method, "includes");
|
||||
return scr_bytes_dyn_index_of(recv->v.bytes, args, argc, strcmp(method, "lastIndexOf") != 0, includes);
|
||||
}
|
||||
return scr_dyn_invoke_prepared(recv, callee, method, args, argc, what);
|
||||
return scr_dyn_invoke_prepared_id(recv, callee, method, mid_slot, args, argc, what);
|
||||
}
|
||||
|
||||
/* toJSON, equals, compare, copy, swap16/32/64, and the refused members. */
|
||||
ScrDyn *scr_dyn_invoke_buffer(ScrDyn *recv, ScrDyn *callee, const char *method, ScrDyn *const *args,
|
||||
size_t argc, const char *what) {
|
||||
ScrDyn *scr_dyn_invoke_buffer(ScrDyn *recv, ScrDyn *callee, const char *method,
|
||||
int32_t *mid_slot, ScrDyn *const *args, size_t argc,
|
||||
const char *what) {
|
||||
if (scr_bytes_dyn_buffer(recv)) {
|
||||
ScrBytes *b = recv->v.bytes;
|
||||
if (!strcmp(method, "toJSON")) return scr_bytes_dyn_to_json(b);
|
||||
@@ -584,5 +590,5 @@ ScrDyn *scr_dyn_invoke_buffer(ScrDyn *recv, ScrDyn *callee, const char *method,
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
return scr_dyn_invoke_prepared(recv, callee, method, args, argc, what);
|
||||
return scr_dyn_invoke_prepared_id(recv, callee, method, mid_slot, args, argc, what);
|
||||
}
|
||||
|
||||
@@ -148,7 +148,7 @@ static void scr_enable_utf8_console_output(void) {
|
||||
}
|
||||
#endif
|
||||
|
||||
void scr_runtime_abi_v27(void) {}
|
||||
void scr_runtime_abi_v28(void) {}
|
||||
|
||||
void scr_init(void) {
|
||||
#ifdef SCR_WORKERS
|
||||
|
||||
@@ -81,7 +81,7 @@ void scr_init(void);
|
||||
/* Program objects emitted by the bundled LLVM helper reference this symbol.
|
||||
* Its versioned spelling makes a mismatched manual runtime link fail before
|
||||
* the program can start. */
|
||||
void scr_runtime_abi_v27(void);
|
||||
void scr_runtime_abi_v28(void);
|
||||
void scr_runtime_workers_v8(void);
|
||||
|
||||
/* ── the trap funnel (scr_console.c; scr_library.c under -DSCR_LIB) ──────
|
||||
|
||||
@@ -1272,15 +1272,19 @@ bool scr_dyn_strict_eq(const ScrDyn *a, const ScrDyn *b);
|
||||
* result owned (+1). MAY THROW (NULL with the exception pending). */
|
||||
ScrDyn *scr_dyn_prepare_method(ScrDyn *recv, const char *method);
|
||||
ScrDyn *scr_dyn_invoke_prepared(ScrDyn *recv, ScrDyn *callee, const char *method, ScrDyn *const *args, size_t argc, const char *what);
|
||||
/* scr_dyn_invoke_prepared for Buffer.prototype method names (scr_bytes_dyn.c),
|
||||
* one entry per method family: a Buffer receiver runs the method, any other
|
||||
* takes the ordinary dispatch. */
|
||||
ScrDyn *scr_dyn_invoke_buffer_num(ScrDyn *recv, ScrDyn *callee, const char *method, ScrDyn *const *args, size_t argc, const char *what);
|
||||
ScrDyn *scr_dyn_invoke_buffer_write(ScrDyn *recv, ScrDyn *callee, const char *method, ScrDyn *const *args, size_t argc, const char *what);
|
||||
ScrDyn *scr_dyn_invoke_buffer_fill(ScrDyn *recv, ScrDyn *callee, const char *method, ScrDyn *const *args, size_t argc, const char *what);
|
||||
ScrDyn *scr_dyn_invoke_buffer_to_string(ScrDyn *recv, ScrDyn *callee, const char *method, ScrDyn *const *args, size_t argc, const char *what);
|
||||
ScrDyn *scr_dyn_invoke_buffer_search(ScrDyn *recv, ScrDyn *callee, const char *method, ScrDyn *const *args, size_t argc, const char *what);
|
||||
ScrDyn *scr_dyn_invoke_buffer(ScrDyn *recv, ScrDyn *callee, const char *method, ScrDyn *const *args, size_t argc, const char *what);
|
||||
/* scr_dyn_invoke_prepared for a call site whose method name is a constant:
|
||||
* *mid_slot, the site's -1-initialized slot, caches the name's method id
|
||||
* after the first call, so later calls skip the name lookup. */
|
||||
ScrDyn *scr_dyn_invoke_prepared_id(ScrDyn *recv, ScrDyn *callee, const char *method, int32_t *mid_slot, ScrDyn *const *args, size_t argc, const char *what);
|
||||
/* scr_dyn_invoke_prepared_id for Buffer.prototype method names
|
||||
* (scr_bytes_dyn.c), one entry per method family: a Buffer receiver runs
|
||||
* the method, any other takes the ordinary dispatch. */
|
||||
ScrDyn *scr_dyn_invoke_buffer_num(ScrDyn *recv, ScrDyn *callee, const char *method, int32_t *mid_slot, ScrDyn *const *args, size_t argc, const char *what);
|
||||
ScrDyn *scr_dyn_invoke_buffer_write(ScrDyn *recv, ScrDyn *callee, const char *method, int32_t *mid_slot, ScrDyn *const *args, size_t argc, const char *what);
|
||||
ScrDyn *scr_dyn_invoke_buffer_fill(ScrDyn *recv, ScrDyn *callee, const char *method, int32_t *mid_slot, ScrDyn *const *args, size_t argc, const char *what);
|
||||
ScrDyn *scr_dyn_invoke_buffer_to_string(ScrDyn *recv, ScrDyn *callee, const char *method, int32_t *mid_slot, ScrDyn *const *args, size_t argc, const char *what);
|
||||
ScrDyn *scr_dyn_invoke_buffer_search(ScrDyn *recv, ScrDyn *callee, const char *method, int32_t *mid_slot, ScrDyn *const *args, size_t argc, const char *what);
|
||||
ScrDyn *scr_dyn_invoke_buffer(ScrDyn *recv, ScrDyn *callee, const char *method, int32_t *mid_slot, ScrDyn *const *args, size_t argc, const char *what);
|
||||
ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method, ScrDyn *const *args, size_t argc, const char *what);
|
||||
/* Keyed read on a FUNC node: the own-property table first (defineProperties
|
||||
* writes land there), then "name" (the box's best-effort static name; ""
|
||||
|
||||
@@ -724,7 +724,7 @@ static bool dyn_bytes_proto_real(int m) {
|
||||
}
|
||||
|
||||
static ScrDyn *scr_dyn_invoke_impl(
|
||||
ScrDyn *recv, const char *method, ScrDyn *const *args, size_t argc,
|
||||
ScrDyn *recv, const char *method, int mid, ScrDyn *const *args, size_t argc,
|
||||
const char *what, ScrDyn *callback_recv);
|
||||
|
||||
/* Live native arrays: the constant-time methods that loops call once per
|
||||
@@ -796,13 +796,26 @@ ScrDyn *scr_dyn_invoke_prepared(ScrDyn *recv, ScrDyn *callee, const char *method
|
||||
ScrDyn *scr_dyn_invoke(ScrDyn *recv, const char *method,
|
||||
ScrDyn *const *args, size_t argc,
|
||||
const char *what) {
|
||||
return scr_dyn_invoke_impl(recv, method, args, argc, what, NULL);
|
||||
return scr_dyn_invoke_impl(recv, method, dyn_method_of(method), args, argc, what, NULL);
|
||||
}
|
||||
|
||||
ScrDyn *scr_dyn_invoke_prepared_id(ScrDyn *recv, ScrDyn *callee, const char *method,
|
||||
int32_t *mid_slot, ScrDyn *const *args, size_t argc,
|
||||
const char *what) {
|
||||
if (callee) return scr_dyn_invoke_prepared(recv, callee, method, args, argc, what);
|
||||
/* The slot is the call site's, shared by every thread: each writes the
|
||||
* same id. */
|
||||
int32_t mid = __atomic_load_n(mid_slot, __ATOMIC_RELAXED);
|
||||
if (mid < 0) {
|
||||
mid = dyn_method_of(method);
|
||||
__atomic_store_n(mid_slot, mid, __ATOMIC_RELAXED);
|
||||
}
|
||||
return scr_dyn_invoke_impl(recv, method, mid, args, argc, what, NULL);
|
||||
}
|
||||
|
||||
static ScrDyn *scr_dyn_invoke_impl(
|
||||
ScrDyn *recv, const char *method, ScrDyn *const *args, size_t argc,
|
||||
ScrDyn *recv, const char *method, int mid, ScrDyn *const *args, size_t argc,
|
||||
const char *what, ScrDyn *callback_recv) {
|
||||
const int mid = dyn_method_of(method);
|
||||
if (recv->kind == SCR_DYN_UNDEF || recv->kind == SCR_DYN_NULL) {
|
||||
ScrJsonBuf b;
|
||||
scr_jb_init(&b);
|
||||
@@ -874,7 +887,7 @@ static ScrDyn *scr_dyn_invoke_impl(
|
||||
bool mutates = materialized->kind == SCR_DYN_ARR &&
|
||||
dyn_arr_proto_mutates(mid);
|
||||
ScrDyn *result = scr_dyn_invoke_impl(
|
||||
materialized, method, args, argc, what, recv);
|
||||
materialized, method, mid, args, argc, what, recv);
|
||||
if (mutates && !scr_exc_pending()) scr_dyn_typed_ref_commit(recv);
|
||||
if (result == materialized) {
|
||||
scr_dyn_release(result);
|
||||
|
||||
@@ -108,7 +108,7 @@ for (const backend of ["llvm"] as const) {
|
||||
object_format: target.objectFormat,
|
||||
minimum_os: target.minimumOs,
|
||||
},
|
||||
runtime_abi: { version: 27, marker: "scr_runtime_abi_v27" },
|
||||
runtime_abi: { version: 28, marker: "scr_runtime_abi_v28" },
|
||||
compiler: { command: "fixture", identity: "fixture", target: target.llvmTriple },
|
||||
macros: { executable: [], excluded: [], sanitizer: "external-toolchain-required" },
|
||||
flavors: {
|
||||
|
||||
Reference in New Issue
Block a user