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
9 changes: 6 additions & 3 deletions modules/dasLLVM/daslib/llvm_jit.das
Original file line number Diff line number Diff line change
Expand Up @@ -1070,10 +1070,13 @@ class public LlvmJitVisitor : AstVisitor {
}

def override preVisitExprBlockExpression(blk : ExprBlock?; expr : ExpressionPtr) : void {
if (current_block_terminates()) {
//! a statement after a terminator - a nested block's return / break / continue, or the
//! constant-condition if the compiler unfolds even unoptimized - lands in its own dead block
LLVMPositionBuilderAtEnd(g_builder, append_basic_block("dead_at_line_{int(expr.at.line)}_"))
}
Comment on lines +1073 to +1077
if (LLVM_DEBUG_TRACES && LLVM_DEBUG_LINE_TRACES) {
if (!current_block_terminates()) {
build_debug_trace(expr.at, FN_JIT_DEBUG_LINE)
}
build_debug_trace(expr.at, FN_JIT_DEBUG_LINE)
}
}

Expand Down
4 changes: 2 additions & 2 deletions modules/dasLLVM/daslib/llvm_jit_run.das
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ var LINK_WHOLE_LIB = false // when true, standalone exe links against the whole
// invalidates cached DLLs (e.g. edits to llvm_jit.das, llvm_macro.das, llvm_jit_common.das,
// runtime helper ABI, default target triple). Cache filenames fold this in, so a bump
// makes every previously written DLL miss the cache on the next run and get GC'd.
let LLVM_JIT_CODEGEN_VERSION : uint64 = 0x75ul // 0x75: the global-offset lookup is memory(none) and emitted at its use site - LLVM dedups and hoists it, an untaken branch never pays it; a solid-context global resolves once per function at entry (0x74: a runtime-only exe emits no register_native_path rows, and a whole-lib exe emits them once (0x73: computed goto lowers to one switch with the trap as its default, not an icmp chain (0x72: policies.fast_math defaults to the host's float flags, so a fast-math host now JITs fast-math (0x71: a CPU class row's cpu is the arch's bare baseline, so a DAS_JIT_BASELINE build enables the row's set and nothing a level implies (0x70: the wasm feature string drops +relaxed-simd and the idot family keeps only the exact extmul + extadd_pairwise lowering on wasm SIMD128 (0x6f: the first wasm idot lowering; 0x6e: the aarch64 SDOT / SMMLA tables gate on DotProd / i8mm, not the arch alone, and the force env reaches the generic exe machine (0x6d: the inline polynomial rail carries NaN: tanh selects the operand back over its ordered clamp, and the sincos quadrant / tan octant convert through llvm.fptosi.sat instead of poisoning on NaN and out-of-range (0x6c: aarch64 vector tan/exp2/log2/log/pow join the inline polynomial rail bit-exactly with the interpreter, sinh/cosh/tanh ride the exp one; 0x6b: aarch64 vector sin/cos ride the inline polynomial; 0x6a: srem/urem for 32-bit %; 0x69: every string argument of an extern is substituted, not just the ones which asked)
let LLVM_JIT_CODEGEN_VERSION : uint64 = 0x76ul // 0x76: a statement after a terminator in the same block list lands in its own dead block instead of after the ret (0x75: the global-offset lookup is memory(none) and emitted at its use site - LLVM dedups and hoists it, an untaken branch never pays it; a solid-context global resolves once per function at entry (0x74: a runtime-only exe emits no register_native_path rows, and a whole-lib exe emits them once (0x73: computed goto lowers to one switch with the trap as its default, not an icmp chain (0x72: policies.fast_math defaults to the host's float flags, so a fast-math host now JITs fast-math (0x71: a CPU class row's cpu is the arch's bare baseline, so a DAS_JIT_BASELINE build enables the row's set and nothing a level implies (0x70: the wasm feature string drops +relaxed-simd and the idot family keeps only the exact extmul + extadd_pairwise lowering on wasm SIMD128 (0x6f: the first wasm idot lowering; 0x6e: the aarch64 SDOT / SMMLA tables gate on DotProd / i8mm, not the arch alone, and the force env reaches the generic exe machine (0x6d: the inline polynomial rail carries NaN: tanh selects the operand back over its ordered clamp, and the sincos quadrant / tan octant convert through llvm.fptosi.sat instead of poisoning on NaN and out-of-range (0x6c: aarch64 vector tan/exp2/log2/log/pow join the inline polynomial rail bit-exactly with the interpreter, sinh/cosh/tanh ride the exp one; 0x6b: aarch64 vector sin/cos ride the inline polynomial; 0x6a: srem/urem for 32-bit %; 0x69: every string argument of an extern is substituted, not just the ones which asked)

// Read by tests-cpp/small/test_jit_emitter_pin.cpp: FNV-1a64 of the emitter sources
// (normalized to LF; file list in the test)
let LLVM_JIT_EMITTER_HASH : uint64 = 0xe10a9a4a639d1e19ul
let LLVM_JIT_EMITTER_HASH : uint64 = 0xe08c02674b3653d9ul

let JIT_FNV_PRIME : uint64 = 1099511628211ul

Expand Down
75 changes: 75 additions & 0 deletions tests/jit_tests/terminated_block_tail.das
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
options gen2
options optimize = false
require dastest/testing_boost

//! every shape here leaves a statement after a terminator inside one block list - the
//! optimizer would delete the tail, so the file compiles unoptimized to keep it

var g_tail_ran = 0

def bare_block_return() : int {
{
return 1
}
g_tail_ran++
return 2
}

def constant_if_return() : int {
if (true) { // nolint:STYLE010 - the constant-condition if is the shape under test
return 3
}
g_tail_ran++
return 4
}

def break_in_nested_block() : int {
var seen = 0
for (_i in range(10)) {
seen++
{
break
}
g_tail_ran++
}
return seen
}

def continue_in_nested_block() : int {
var seen = 0
for (_i in range(3)) {
{
seen++
continue
}
g_tail_ran++
}
return seen
}

def void_bare_block_return() {
{
return
}
g_tail_ran++
}

[test]
def test_statements_after_a_terminated_nested_block(t : T?) {
g_tail_ran = 0
t |> equal(bare_block_return(), 1)
t |> equal(constant_if_return(), 3)
t |> equal(break_in_nested_block(), 1)
t |> equal(continue_in_nested_block(), 3)
void_bare_block_return()
t |> equal(g_tail_ran, 0, "a statement after a terminator never runs")
}

[test]
def test_skip_shape_returns_early(t : T?) {
if (true) { // nolint:STYLE010 - the gated-skip shape a test file writes
t |> skip("the early return is the shape under test")
return
}
t |> failure("the tail after the early return ran")
}
3 changes: 2 additions & 1 deletion tests/lint/_fixture_dim_expr_folding.das
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ let SPAN = ROWS * STEP

// A vector constructor is a call, so this one folds through neither the operators nor
// an already-constant node -- the shape a shader spells a basis matrix's column in.
let VEC_HALF = float4(1.0, 2.0, 3.0, 4.0) / 2.0
//! a multiply, not a divide: a -ffast-math host divides a vector by a runtime splat through rcpps, one ULP off
let VEC_HALF = float4(1.0, 2.0, 3.0, 4.0) * 0.5

// A flat sum nests one operator per term, so this one is deeper than the bound that
// stops a cycle of consts defined off each other. Depth in the AST is not a cycle.
Expand Down
Loading