Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions .github/workflows/pages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,14 @@ jobs:
cp web/output/daslang_static.wasm _site/playground/
cp web/output/daslang_static.js _site/files/wasm/
cp web/output/daslang_static.wasm _site/files/wasm/
# Copy sample data if present
cp -r web/examples/ui/samples _site/playground/samples 2>/dev/null || true
# Pull samples from site/playground/samples — that directory is the
# single staged source of truth: stage_site_playground populates
# the .das sources, stage_site_playground_wasm overlays the
# cross-compiled .wasm artifacts. The HEAD probe in main.js
# (updateEngineAvailability) enables the JIT radio only when the
# .wasm is reachable, so dropping the .wasm here permanently
# disables the toggle in production.
cp -r site/playground/samples _site/playground/samples 2>/dev/null || true
# Our Forge-skinned playground/index.html + skin CSS + init shim take precedence.
# CodeMirror bundle (codemirror.min.js/css, simple-mode.js, daslang-*.js,
# cm-forge.css) is served from /files/cm/ — copied via `cp -r site/files`
Expand Down
386 changes: 160 additions & 226 deletions daslib/linq_fold.das

Large diffs are not rendered by default.

19 changes: 14 additions & 5 deletions modules/dasLLVM/daslib/llvm_exe.das
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,12 @@ class CollectExternVisitor : AstVisitor {
LLVMBuildCall2(init_builder, register_mod_type, reg_strings, array<LLVMValueRef>(), "")
registered_modules["strings"] = true

// void * get_jit_table_{at,find,erase} ( int32_t baseType, Context * context, LineInfoArg * at )
reg_extern_tab_type = LLVMFunctionType(types.LLVMVoidPtrType(),
fixed_array<LLVMTypeRef>(
types.t_int32, // type
types.t_int32, // baseType
types.LLVMVoidPtrType(), // context
types.LLVMVoidPtrType(), // at
)
)
tab_at_accessor = LLVMAddFunctionWithType(g_mod, "get_jit_table_at", reg_extern_tab_type)
Expand Down Expand Up @@ -420,9 +423,12 @@ class CollectExternVisitor : AstVisitor {
}
}

// get_jit_table_{at,find,erase} dispatch on baseType only; context+at unused.
// Pass null for both to match the C++ signature without a real Context.
def add_table_at_call(index_t : TypeDeclPtr) {
let typ = get_table_t(index_t)
var call_args = array<LLVMValueRef>(types.ConstI32(uint64(typ)))
let null_ptr = LLVMConstPointerNull(types.LLVMVoidPtrType())
var call_args = array<LLVMValueRef>(types.ConstI32(uint64(typ)), null_ptr, null_ptr)
let tab_ptr = LLVMBuildCall2(builder, reg_extern_tab_type, tab_at_accessor, call_args, "")

let tab_method = LLVMGetNamedGlobal(g_mod, DllName(FN_JIT_TABLE_AT(typ)).glob())
Expand All @@ -431,7 +437,8 @@ class CollectExternVisitor : AstVisitor {

def add_table_find_call(index_t : TypeDeclPtr) {
let typ = get_table_t(index_t)
var call_args = array<LLVMValueRef>(types.ConstI32(uint64(typ)))
let null_ptr = LLVMConstPointerNull(types.LLVMVoidPtrType())
var call_args = array<LLVMValueRef>(types.ConstI32(uint64(typ)), null_ptr, null_ptr)
let tab_ptr = LLVMBuildCall2(builder, reg_extern_tab_type, tab_find_accessor, call_args, "")

let tab_method = LLVMGetNamedGlobal(g_mod, DllName(FN_JIT_TABLE_FIND(typ)).glob())
Expand All @@ -440,7 +447,8 @@ class CollectExternVisitor : AstVisitor {

def add_table_erase_call(index_t : TypeDeclPtr) {
let typ = get_table_t(index_t)
var call_args = array<LLVMValueRef>(types.ConstI32(uint64(typ)))
let null_ptr = LLVMConstPointerNull(types.LLVMVoidPtrType())
var call_args = array<LLVMValueRef>(types.ConstI32(uint64(typ)), null_ptr, null_ptr)
let tab_ptr = LLVMBuildCall2(builder, reg_extern_tab_type, tab_erase_accessor, call_args, "")

let tab_method = LLVMGetNamedGlobal(g_mod, DllName(FN_JIT_TABLE_ERASE(typ)).glob())
Expand Down Expand Up @@ -978,7 +986,8 @@ def public inject_main(program_context : Context?; ctx : LLVMContextRef;
LLVMBuildCall2(builder, reg_fusion_type, reg_fusion_fn, array<LLVMValueRef>(), "")
}

let init_global_var_type = LLVMFunctionType(types.LLVMVoidPtrType(),
// void jit_register_standalone_variable ( Context * ctx, uint64_t mnh, uint64_t offset )
let init_global_var_type = LLVMFunctionType(types.t_void,
fixed_array<LLVMTypeRef>(
types.LLVMVoidPtrType(), // Context *
types.t_int64, // uint64_t mnh
Expand Down
84 changes: 63 additions & 21 deletions modules/dasLLVM/daslib/llvm_jit.das
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,9 @@ class public LlvmJitVisitor : AstVisitor {
def build_noalias_list(annotations : AnnotationList) : tuple<noalias : table<string>; nocapture : table<string>> {
var noalias : table<string>
var nocapture : table<string>
// Skip user hints entirely on wasm32 — host-LLVM wasm codegen has had
// miscompiles around them. Tracked alongside the alwaysinline gotcha.
if (g_target_is_wasm) return <- (noalias <- noalias, nocapture <- nocapture)
for (ann in annotations) {
if (ann.annotation.name == "hint") {
for (arg in ann.arguments) {
Expand Down Expand Up @@ -506,6 +509,10 @@ class public LlvmJitVisitor : AstVisitor {
option_no_range_check = false
option_no_alias = false
option_no_capture = false
// Bypass user hints on wasm — keeps option_no_* flags false (= safe
// defaults: bounds checks on, no alias/capture assumption) and skips
// every LLVMAddAttributeToFunction call below. See g_target_is_wasm.
if (g_target_is_wasm) return
for (ann in annotations) {
if (ann.annotation.name == "hint") {
for (arg in ann.arguments) {
Expand Down Expand Up @@ -820,7 +827,8 @@ class public LlvmJitVisitor : AstVisitor {
get_line_info_ptr(funAt),
types.ConstI32(uint64(totalStackSize)),
prologue,
get_context_param()
get_context_param(),
get_line_info_ptr(funAt) // at — used for stack-overflow throw
)
var typ = g_fn_types[FN_JIT_PROLOGUE]
LLVMBuildCall2(g_builder, typ, LLVMGetNamedFunction(g_mod, FN_JIT_PROLOGUE), params, "")
Expand Down Expand Up @@ -3013,9 +3021,13 @@ class public LlvmJitVisitor : AstVisitor {
LLVMBuildStore(g_builder, s0, getV(svar))
let dimSize = ssrc._type.dim[0]
var tail = types.ConstI32(uint64(dimSize - 1))
var sto = build_array_index(sdim, tail, etype, "last_element")
sto = LLVMBuildPtrToInt(g_builder, sto, types.LLVMIntPtrType(), "dim_end")
range2[ssrc] = sto
// Stash end-element pointer as-is; compare directly in next-step.
// Previous code ptrtoint'd to LLVMIntPtrType which is statically i64
// on a 64-bit host even when codegen target is wasm32 — that zero-
// extends a 32-bit ptr to 64 bits but wasm32 codegen lowers it in a
// way that breaks the i64 equality check. Direct ptr compare avoids
// the width mismatch entirely.
range2[ssrc] = build_array_index(sdim, tail, etype, "last_element")
} elif (ssrc._type.isGoodArrayType) {// {}->first()
var arr = LLVMBuildLoadData2Aligned(g_builder, type_to_llvm_type(ssrc._type), getE(ssrc), ssrc._type.alignOf, "array")
var size = LLVMBuildExtractValue(g_builder, arr, uint(JIT_ARRAY.SIZE), "array.size")
Expand All @@ -3034,9 +3046,8 @@ class public LlvmJitVisitor : AstVisitor {
var data = LLVMBuildExtractValue(g_builder, arr, uint(JIT_ARRAY.DATA), "array.data")
LLVMBuildStore(g_builder, data, getV(svar))
var tail = LLVMBuildSub(g_builder, size, i64_one, "")
var sto = build_array_index(data, tail, ssrc._type.firstType, "last_element")
sto = LLVMBuildPtrToInt(g_builder, sto, types.LLVMIntPtrType(), "array_end")
range2[ssrc] = sto
// See dim case above — store end-element ptr directly, compare-by-ptr.
range2[ssrc] = build_array_index(data, tail, ssrc._type.firstType, "last_element")
} elif (ssrc._type.isIterator) {
if (is_count_or_ucount(ssrc)) {
var ccount = ssrc as ExprCall
Expand Down Expand Up @@ -3132,19 +3143,19 @@ class public LlvmJitVisitor : AstVisitor {
var svar_v = LLVMBuildLoad2(g_builder, LLVMPointerType(type_to_llvm_type(svar._type), 0u), getV(svar), "")
var svar_i = build_array_index(svar_v, types.ConstI32(1ul), etype, "next_element")
LLVMBuildStore(g_builder, svar_i, getV(svar))
// Direct ptr compare — avoids LLVMIntPtrType host/wasm32 width mismatch.
var sto = range2[ssrc]
svar_i = LLVMBuildPtrToInt(g_builder, svar_v, types.LLVMIntPtrType(), "")
var rcond = LLVMBuildICmp(g_builder, LLVMIntPredicate.LLVMIntEQ, svar_i, sto, "")
var rcond = LLVMBuildICmp(g_builder, LLVMIntPredicate.LLVMIntEQ, svar_v, sto, "")
var nextOk = append_basic_block("for_{svar.name}_next_ok")
LLVMBuildCondBr(g_builder, rcond, lblk.loop_end, nextOk)
LLVMPositionBuilderAtEnd(g_builder, nextOk)
} elif (ssrc._type.isGoodArrayType) {// {}->next()
var svar_v = LLVMBuildLoad2(g_builder, LLVMPointerType(type_to_llvm_type(svar._type), 0u), getV(svar), "")
var svar_i = build_array_index(svar_v, types.ConstI32(1ul), ssrc._type.firstType, "next_element")
LLVMBuildStore(g_builder, svar_i, getV(svar))
// Direct ptr compare — avoids LLVMIntPtrType host/wasm32 width mismatch.
var sto = range2[ssrc]
svar_i = LLVMBuildPtrToInt(g_builder, svar_v, types.LLVMIntPtrType(), "")
var rcond = LLVMBuildICmp(g_builder, LLVMIntPredicate.LLVMIntEQ, svar_i, sto, "")
var rcond = LLVMBuildICmp(g_builder, LLVMIntPredicate.LLVMIntEQ, svar_v, sto, "")
var nextOk = append_basic_block("for_{svar.name}_next_ok")
LLVMBuildCondBr(g_builder, rcond, lblk.loop_end, nextOk)
LLVMPositionBuilderAtEnd(g_builder, nextOk)
Expand Down Expand Up @@ -4758,10 +4769,25 @@ class public LlvmJitVisitor : AstVisitor {
vptr = get_global_offset_by_mnh(mnh, expr.variable.flags.global_shared, "global_var_{expr.variable.name}_mnh_ptr")
vptr = LLVMBuildPointerCast(g_builder, vptr, get_type_pointer(expr.variable._type), "global_var_{expr.variable.name}")
} else {
var ctx_ptr = LLVMBuildPointerCast(g_builder, get_context_param(), LLVMPointerType(types.t_int8, 0u), "")
let coffset = expr.variable.flags.global_shared ? CONTEXT_OFFSET_OF_SHARED : CONTEXT_OFFSET_OF_GLOBALS
var global_ptr = LLVMBuildGEP2(g_builder, types.t_int8, ctx_ptr, types.ConstI32(uint64(coffset)), "")
global_ptr = LLVMBuildLoad2(g_builder, LLVMPointerType(types.t_int8, 0u), global_ptr, "")
// On wasm32 the host's offsetof(Context, globals) (the
// CONTEXT_OFFSET_OF_GLOBALS constant baked into module_jit.cpp)
// does NOT match the wasm32 runtime's Context layout —
// 4-byte pointers shift everything. Call into the runtime
// archive (which knows its own layout) instead of doing
// GEP+load against a host-derived offset. Native keeps
// the inlined GEP+load — same codegen as before.
var global_ptr : LLVMOpaqueValue?
if (g_target_is_wasm) {
let fname = expr.variable.flags.global_shared ? FN_JIT_GET_SHARED_BASE : FN_JIT_GET_GLOBALS_BASE
let fty = g_fn_types[fname]
var args = fixed_array(get_context_param())
global_ptr = LLVMBuildCall2(g_builder, fty, LLVMGetNamedFunction(g_mod, fname), args, "")
} else {
var ctx_ptr = LLVMBuildPointerCast(g_builder, get_context_param(), LLVMPointerType(types.t_int8, 0u), "")
let coffset = expr.variable.flags.global_shared ? CONTEXT_OFFSET_OF_SHARED : CONTEXT_OFFSET_OF_GLOBALS
var gp = LLVMBuildGEP2(g_builder, types.t_int8, ctx_ptr, types.ConstI32(uint64(coffset)), "")
global_ptr = LLVMBuildLoad2(g_builder, LLVMPointerType(types.t_int8, 0u), gp, "")
}
vptr = LLVMBuildGEP2(g_builder, types.t_int8, global_ptr, types.ConstI32(uint64(expr.variable.stackTop)), "")
}
tryV = LLVMBuildPointerCast(g_builder, vptr, get_type_pointer(expr.variable._type), "global_var_{expr.variable.name}")
Expand Down Expand Up @@ -6065,8 +6091,14 @@ class public LlvmJitVisitor : AstVisitor {
operands |> push(temp)
var mustprog = make_loop_md_flag("llvm.loop.mustprogress")
operands |> push(mustprog)
for (a in args) {
append_loop_hint_operand(operands, a)
// Skip user-supplied loop hints on wasm — host LLVM's wasm32 backend
// has had miscompiles around llvm.loop.* metadata in combination with
// certain bodies. mustprogress alone is preserved (safe, required for
// some opt passes). See g_target_is_wasm.
if (!g_target_is_wasm) {
for (a in args) {
append_loop_hint_operand(operands, a)
}
}
let loop_md = LLVMMDNodeInContext2(g_ctx, array_data_ptr(operands), uint64(length(operands)))
LLVMMetadataReplaceAllUsesWith(temp, loop_md)
Expand Down Expand Up @@ -6598,10 +6630,20 @@ def public generate_globals_initialization_fn(ctx : Context?; prog : Program?;
vptr = LLVMBuildCall2(visitor.g_builder, typ, func, params, "global_var_{glob.name}_mnh_ptr")
vptr = LLVMBuildPointerCast(visitor.g_builder, vptr, get_type_pointer(glob._type), "global_var_{glob.name}")
} else {
var ctx_ptr = LLVMBuildPointerCast(visitor.g_builder, ctx_param, LLVMPointerType(types.t_int8, 0u), "")
let coffset = glob.flags.global_shared ? CONTEXT_OFFSET_OF_SHARED : CONTEXT_OFFSET_OF_GLOBALS
var global_ptr = LLVMBuildGEP2(visitor.g_builder, types.t_int8, ctx_ptr, types.ConstI32(uint64(coffset)), "")
global_ptr = LLVMBuildLoad2(visitor.g_builder, LLVMPointerType(types.t_int8, 0u), global_ptr, "")
// See the matching site in preVisitExprVar for why
// wasm32 goes through a runtime helper here.
var global_ptr : LLVMOpaqueValue?
if (g_target_is_wasm) {
let fname = glob.flags.global_shared ? FN_JIT_GET_SHARED_BASE : FN_JIT_GET_GLOBALS_BASE
let fty = g_fn_types[fname]
var args = fixed_array(ctx_param)
global_ptr = LLVMBuildCall2(visitor.g_builder, fty, LLVMGetNamedFunction(g_mod, fname), args, "")
} else {
var ctx_ptr = LLVMBuildPointerCast(visitor.g_builder, ctx_param, LLVMPointerType(types.t_int8, 0u), "")
let coffset = glob.flags.global_shared ? CONTEXT_OFFSET_OF_SHARED : CONTEXT_OFFSET_OF_GLOBALS
var gp = LLVMBuildGEP2(visitor.g_builder, types.t_int8, ctx_ptr, types.ConstI32(uint64(coffset)), "")
global_ptr = LLVMBuildLoad2(visitor.g_builder, LLVMPointerType(types.t_int8, 0u), gp, "")
}
vptr = LLVMBuildGEP2(visitor.g_builder, types.t_int8, global_ptr, types.ConstI32(uint64(glob.stackTop)), "")
}
// register CMRES destination so init expressions that return by copy/move
Expand Down
Loading
Loading