diff --git a/apps/tests/ny-plugin-ret-llvm-smoke/main.nyash b/apps/tests/ny-plugin-ret-llvm-smoke/main.nyash new file mode 100644 index 00000000..1a4a3447 --- /dev/null +++ b/apps/tests/ny-plugin-ret-llvm-smoke/main.nyash @@ -0,0 +1,15 @@ +// ny-plugin-ret-llvm-smoke +// Purpose: Verify plugin return values are preserved through assign/print +// - CounterBox.get() returns integer; return it from main (AOT prints Result: ) +// - StringBox.concat returns string; print it via print() + +local c = new CounterBox() +c.inc() +local v = c.get() + +local s = new StringBox("ab") +local t = s.concat("CD") +print("S=" + t) + +return v + diff --git a/src/backend/llvm/compiler/codegen/instructions.rs b/src/backend/llvm/compiler/codegen/instructions.rs index 35b4b203..4b6d71aa 100644 --- a/src/backend/llvm/compiler/codegen/instructions.rs +++ b/src/backend/llvm/compiler/codegen/instructions.rs @@ -35,6 +35,150 @@ pub(super) fn create_basic_blocks<'ctx>( (bb_map, entry_bb) } +// Pre-create PHI nodes for all blocks; also inserts placeholder values into vmap. +pub(super) fn precreate_phis<'ctx>( + codegen: &CodegenContext<'ctx>, + func: &MirFunction, + bb_map: &HashMap>, + vmap: &mut HashMap>, +) -> Result< + HashMap< + BasicBlockId, + Vec<(ValueId, PhiValue<'ctx>, Vec<(BasicBlockId, ValueId)>)>, + >, + String, +> { + use crate::mir::instruction::MirInstruction; + use super::types::map_mirtype_to_basic; + let mut phis_by_block: HashMap< + BasicBlockId, + Vec<(ValueId, PhiValue<'ctx>, Vec<(BasicBlockId, ValueId)>)>, + > = HashMap::new(); + for bid in func.block_ids() { + let bb = *bb_map.get(&bid).ok_or("missing bb in map")?; + codegen.builder.position_at_end(bb); + let block = func.blocks.get(&bid).unwrap(); + for inst in block + .instructions + .iter() + .take_while(|i| matches!(i, MirInstruction::Phi { .. })) + { + if let MirInstruction::Phi { dst, inputs } = inst { + let mut phi_ty: Option = None; + if let Some(mt) = func.metadata.value_types.get(dst) { + phi_ty = Some(map_mirtype_to_basic(codegen.context, mt)); + } else if let Some((_, iv)) = inputs.first() { + if let Some(mt) = func.metadata.value_types.get(iv) { + phi_ty = Some(map_mirtype_to_basic(codegen.context, mt)); + } + } + let phi_ty = phi_ty.unwrap_or_else(|| codegen.context.i64_type().into()); + let phi = codegen + .builder + .build_phi(phi_ty, &format!("phi_{}", dst.as_u32())) + .map_err(|e| e.to_string())?; + vmap.insert(*dst, phi.as_basic_value()); + phis_by_block + .entry(bid) + .or_default() + .push((*dst, phi, inputs.clone())); + } + } + } + Ok(phis_by_block) +} + +// Lower Store: handle allocas with element type tracking and integer width adjust +pub(super) fn lower_store<'ctx>( + codegen: &CodegenContext<'ctx>, + vmap: &HashMap>, + allocas: &mut HashMap>, + alloca_elem_types: &mut HashMap>, + value: &ValueId, + ptr: &ValueId, +) -> Result<(), String> { + use inkwell::types::BasicTypeEnum; + let val = *vmap.get(value).ok_or("store value missing")?; + let elem_ty = match val { + BasicValueEnum::IntValue(iv) => BasicTypeEnum::IntType(iv.get_type()), + BasicValueEnum::FloatValue(fv) => BasicTypeEnum::FloatType(fv.get_type()), + BasicValueEnum::PointerValue(pv) => BasicTypeEnum::PointerType(pv.get_type()), + _ => return Err("unsupported store value type".to_string()), + }; + if let Some(existing) = allocas.get(ptr).copied() { + let existing_elem = *alloca_elem_types.get(ptr).ok_or("alloca elem type missing")?; + if existing_elem != elem_ty { + match (val, existing_elem) { + (BasicValueEnum::IntValue(iv), BasicTypeEnum::IntType(t)) => { + let bw_src = iv.get_type().get_bit_width(); + let bw_dst = t.get_bit_width(); + if bw_src < bw_dst { + let adj = codegen.builder.build_int_z_extend(iv, t, "zext").map_err(|e| e.to_string())?; + codegen.builder.build_store(existing, adj).map_err(|e| e.to_string())?; + } else if bw_src > bw_dst { + let adj = codegen.builder.build_int_truncate(iv, t, "trunc").map_err(|e| e.to_string())?; + codegen.builder.build_store(existing, adj).map_err(|e| e.to_string())?; + } else { + codegen.builder.build_store(existing, iv).map_err(|e| e.to_string())?; + } + } + (BasicValueEnum::PointerValue(pv), BasicTypeEnum::PointerType(pt)) => { + let adj = codegen.builder.build_pointer_cast(pv, pt, "pcast").map_err(|e| e.to_string())?; + codegen.builder.build_store(existing, adj).map_err(|e| e.to_string())?; + } + (BasicValueEnum::FloatValue(fv), BasicTypeEnum::FloatType(ft)) => { + // Only f64 currently expected + if fv.get_type() != ft { return Err("float width mismatch in store".to_string()); } + codegen.builder.build_store(existing, fv).map_err(|e| e.to_string())?; + } + _ => return Err("store type mismatch".to_string()), + } + } else { + codegen.builder.build_store(existing, val).map_err(|e| e.to_string())?; + } + } else { + let slot = codegen + .builder + .build_alloca(elem_ty, &format!("slot_{}", ptr.as_u32())) + .map_err(|e| e.to_string())?; + codegen.builder.build_store(slot, val).map_err(|e| e.to_string())?; + allocas.insert(*ptr, slot); + alloca_elem_types.insert(*ptr, elem_ty); + } + Ok(()) +} + +pub(super) fn lower_load<'ctx>( + codegen: &CodegenContext<'ctx>, + vmap: &mut HashMap>, + allocas: &mut HashMap>, + alloca_elem_types: &mut HashMap>, + dst: &ValueId, + ptr: &ValueId, +) -> Result<(), String> { + use inkwell::types::BasicTypeEnum; + let (slot, elem_ty) = if let Some(s) = allocas.get(ptr).copied() { + let et = *alloca_elem_types.get(ptr).ok_or("alloca elem type missing")?; + (s, et) + } else { + // Default new slot as i64 for uninitialized loads + let i64t = codegen.context.i64_type(); + let slot = codegen + .builder + .build_alloca(i64t, &format!("slot_{}", ptr.as_u32())) + .map_err(|e| e.to_string())?; + allocas.insert(*ptr, slot); + alloca_elem_types.insert(*ptr, i64t.into()); + (slot, i64t.into()) + }; + let lv = codegen + .builder + .build_load(elem_ty, slot, &format!("load_{}", dst.as_u32())) + .map_err(|e| e.to_string())?; + vmap.insert(*dst, lv); + Ok(()) +} + // Const lowering: produce a BasicValue and store into vmap pub(super) fn lower_const<'ctx>( codegen: &CodegenContext<'ctx>, diff --git a/src/backend/llvm/compiler/codegen/mod.rs b/src/backend/llvm/compiler/codegen/mod.rs index b8b9b0b9..10fcb734 100644 --- a/src/backend/llvm/compiler/codegen/mod.rs +++ b/src/backend/llvm/compiler/codegen/mod.rs @@ -1209,179 +1209,10 @@ impl LLVMCompiler { vmap.insert(*dst, out); } MirInstruction::Store { value, ptr } => { - let val = *vmap.get(value).ok_or("store value missing")?; - // Determine or create the alloca for this ptr, using current value type - let elem_ty = match val { - BasicValueEnum::IntValue(iv) => BasicTypeEnum::IntType(iv.get_type()), - BasicValueEnum::FloatValue(fv) => { - BasicTypeEnum::FloatType(fv.get_type()) - } - BasicValueEnum::PointerValue(pv) => { - BasicTypeEnum::PointerType(pv.get_type()) - } - _ => return Err("unsupported store value type".to_string()), - }; - if let Some(existing) = allocas.get(ptr).copied() { - // If types mismatch (e.g., i1 vs i64), try simple widen/narrow for ints; pointer->pointer cast - let existing_elem = *alloca_elem_types - .get(ptr) - .ok_or("alloca elem type missing")?; - if existing_elem != elem_ty { - match (val, existing_elem) { - (BasicValueEnum::IntValue(iv), BasicTypeEnum::IntType(t)) => { - let bw_src = iv.get_type().get_bit_width(); - let bw_dst = t.get_bit_width(); - if bw_src < bw_dst { - let adj = codegen - .builder - .build_int_z_extend(iv, t, "zext") - .map_err(|e| e.to_string())?; - codegen - .builder - .build_store(existing, adj) - .map_err(|e| e.to_string())?; - } else if bw_src > bw_dst { - let adj = codegen - .builder - .build_int_truncate(iv, t, "trunc") - .map_err(|e| e.to_string())?; - codegen - .builder - .build_store(existing, adj) - .map_err(|e| e.to_string())?; - } else { - codegen - .builder - .build_store(existing, iv) - .map_err(|e| e.to_string())?; - } - } - ( - BasicValueEnum::PointerValue(pv), - BasicTypeEnum::PointerType(pt), - ) => { - let adj = codegen - .builder - .build_pointer_cast(pv, pt, "pcast") - .map_err(|e| e.to_string())?; - codegen - .builder - .build_store(existing, adj) - .map_err(|e| e.to_string())?; - } - ( - BasicValueEnum::FloatValue(fv), - BasicTypeEnum::FloatType(ft), - ) => { - if fv.get_type() == ft { - codegen - .builder - .build_store(existing, fv) - .map_err(|e| e.to_string())?; - } else { - return Err("float width mismatch in store".to_string()); - } - } - _ => return Err("store type mismatch".to_string()), - }; - } else { - match val { - BasicValueEnum::IntValue(iv) => { - codegen - .builder - .build_store(existing, iv) - .map_err(|e| e.to_string())?; - } - BasicValueEnum::FloatValue(fv) => { - codegen - .builder - .build_store(existing, fv) - .map_err(|e| e.to_string())?; - } - BasicValueEnum::PointerValue(pv) => { - codegen - .builder - .build_store(existing, pv) - .map_err(|e| e.to_string())?; - } - _ => return Err("unsupported store value type".to_string()), - } - } - } else { - // Create new alloca at entry - let slot = entry_builder - .build_alloca(elem_ty, &format!("slot_{}", ptr.as_u32())) - .map_err(|e| e.to_string())?; - // Initialize to zero/null - let zero_val: BasicValueEnum = match elem_ty { - BasicTypeEnum::IntType(t) => t.const_zero().into(), - BasicTypeEnum::FloatType(t) => t.const_float(0.0).into(), - BasicTypeEnum::PointerType(t) => t.const_zero().into(), - _ => return Err("Unsupported alloca element type".to_string()), - }; - entry_builder - .build_store(slot, zero_val) - .map_err(|e| e.to_string())?; - allocas.insert(*ptr, slot); - alloca_elem_types.insert(*ptr, elem_ty); - match val { - BasicValueEnum::IntValue(iv) => { - codegen - .builder - .build_store(slot, iv) - .map_err(|e| e.to_string())?; - } - BasicValueEnum::FloatValue(fv) => { - codegen - .builder - .build_store(slot, fv) - .map_err(|e| e.to_string())?; - } - BasicValueEnum::PointerValue(pv) => { - codegen - .builder - .build_store(slot, pv) - .map_err(|e| e.to_string())?; - } - _ => return Err("unsupported store value type".to_string()), - } - } + instructions::lower_store(&codegen, &vmap, &mut allocas, &mut alloca_elem_types, value, ptr)?; } MirInstruction::Load { dst, ptr } => { - // Ensure alloca exists; if not, try to infer from annotated dst type, else default i64 - let (slot, elem_ty) = if let Some(p) = allocas.get(ptr).copied() { - let ety = *alloca_elem_types - .get(ptr) - .ok_or("alloca elem type missing")?; - (p, ety) - } else { - let elem_ty = if let Some(mt) = func.metadata.value_types.get(dst) { - map_mirtype_to_basic(codegen.context, mt) - } else { - codegen.context.i64_type().into() - }; - // Create new alloca at entry - let slot = entry_builder - .build_alloca(elem_ty, &format!("slot_{}", ptr.as_u32())) - .map_err(|e| e.to_string())?; - let zero_val: BasicValueEnum = match elem_ty { - BasicTypeEnum::IntType(t) => t.const_zero().into(), - BasicTypeEnum::FloatType(t) => t.const_float(0.0).into(), - BasicTypeEnum::PointerType(t) => t.const_zero().into(), - _ => return Err("Unsupported alloca element type".to_string()), - }; - entry_builder - .build_store(slot, zero_val) - .map_err(|e| e.to_string())?; - allocas.insert(*ptr, slot); - alloca_elem_types.insert(*ptr, elem_ty); - (slot, elem_ty) - }; - let lv = codegen - .builder - .build_load(elem_ty, slot, &format!("load_{}", dst.as_u32())) - .map_err(|e| e.to_string())?; - vmap.insert(*dst, lv); + instructions::lower_load(&codegen, &mut vmap, &mut allocas, &mut alloca_elem_types, dst, ptr)?; } MirInstruction::Phi { .. } => { // Already created in pre-pass; nothing to do here. diff --git a/tools/llvm_smoke.sh b/tools/llvm_smoke.sh index e8fd1eb4..0c4787d5 100644 --- a/tools/llvm_smoke.sh +++ b/tools/llvm_smoke.sh @@ -17,12 +17,17 @@ if ! command -v llvm-config-18 >/dev/null 2>&1; then exit 2 fi +# Resolve LLVM 18 prefix once and export for both 180/181 variants used by llvm-sys +_LLVMPREFIX=$(llvm-config-18 --prefix) +export LLVM_SYS_181_PREFIX="${_LLVMPREFIX}" +export LLVM_SYS_180_PREFIX="${_LLVMPREFIX}" + # --- AOT smoke: apps/ny-llvm-bitops (bitwise & shift operations) --- if [[ "${NYASH_LLVM_BITOPS_SMOKE:-0}" == "1" ]]; then echo "[llvm-smoke] building + linking apps/ny-llvm-bitops ..." >&2 OBJ_BIT="$PWD/target/aot_objects/bitops_smoke.o" rm -f "$OBJ_BIT" - NYASH_LLVM_OBJ_OUT="$OBJ_BIT" LLVM_SYS_181_PREFIX="${_LLVMPREFIX}" LLVM_SYS_180_PREFIX="${_LLVMPREFIX}" "$BIN" --backend llvm apps/tests/ny-llvm-bitops/main.nyash >/dev/null || true + NYASH_LLVM_OBJ_OUT="$OBJ_BIT" "$BIN" --backend llvm apps/tests/ny-llvm-bitops/main.nyash >/dev/null || true NYASH_LLVM_SKIP_EMIT=1 NYASH_LLVM_OBJ_OUT="$OBJ_BIT" ./tools/build_llvm.sh apps/tests/ny-llvm-bitops/main.nyash -o app_bitops_llvm >/dev/null || true echo "[llvm-smoke] running app_bitops_llvm ..." >&2 out_bit=$(./app_bitops_llvm || true) @@ -38,12 +43,11 @@ fi echo "[llvm-smoke] building nyash (${MODE}, feature=llvm)..." >&2 # Support both llvm-sys 180/181 by exporting both prefixes to the same value -_LLVMPREFIX=$(llvm-config-18 --prefix) -LLVM_SYS_181_PREFIX="${_LLVMPREFIX}" LLVM_SYS_180_PREFIX="${_LLVMPREFIX}" cargo build -q ${MODE:+--${MODE}} --features llvm +cargo build -q ${MODE:+--${MODE}} --features llvm echo "[llvm-smoke] running --backend llvm on examples/llvm11_core_smoke.nyash ..." >&2 rm -f "$OBJ" -NYASH_LLVM_OBJ_OUT="$OBJ" LLVM_SYS_181_PREFIX="${_LLVMPREFIX}" LLVM_SYS_180_PREFIX="${_LLVMPREFIX}" "$BIN" --backend llvm examples/llvm11_core_smoke.nyash >/dev/null || true +NYASH_LLVM_OBJ_OUT="$OBJ" "$BIN" --backend llvm examples/llvm11_core_smoke.nyash >/dev/null || true if [[ ! -f "$OBJ" ]]; then echo "error: expected object not found: $OBJ" >&2 @@ -62,7 +66,7 @@ if [[ "${NYASH_LLVM_ARRAY_SMOKE:-0}" == "1" ]]; then # Pre-emit object explicitly (more stable) OBJ_ARRAY="$PWD/target/aot_objects/array_smoke.o" rm -f "$OBJ_ARRAY" - NYASH_LLVM_OBJ_OUT="$OBJ_ARRAY" LLVM_SYS_181_PREFIX="${_LLVMPREFIX}" LLVM_SYS_180_PREFIX="${_LLVMPREFIX}" "$BIN" --backend llvm apps/tests/ny-llvm-smoke/main.nyash >/dev/null || true + NYASH_LLVM_OBJ_OUT="$OBJ_ARRAY" "$BIN" --backend llvm apps/tests/ny-llvm-smoke/main.nyash >/dev/null || true NYASH_LLVM_SKIP_EMIT=1 NYASH_LLVM_OBJ_OUT="$OBJ_ARRAY" ./tools/build_llvm.sh apps/tests/ny-llvm-smoke/main.nyash -o app_link >/dev/null echo "[llvm-smoke] running app_link ..." >&2 out_smoke=$(./app_link || true) @@ -84,7 +88,7 @@ if [[ "${NYASH_LLVM_ARRAY_RET_SMOKE:-0}" == "1" ]] && [[ "${NYASH_DISABLE_PLUGIN fi OBJ_AR="$PWD/target/aot_objects/array_ret_smoke.o" rm -f "$OBJ_AR" - NYASH_LLVM_OBJ_OUT="$OBJ_AR" LLVM_SYS_181_PREFIX="${_LLVMPREFIX}" LLVM_SYS_180_PREFIX="${_LLVMPREFIX}" "$BIN" --backend llvm apps/tests/ny-array-llvm-ret/main.nyash >/dev/null || true + NYASH_LLVM_OBJ_OUT="$OBJ_AR" "$BIN" --backend llvm apps/tests/ny-array-llvm-ret/main.nyash >/dev/null || true NYASH_LLVM_SKIP_EMIT=1 NYASH_LLVM_OBJ_OUT="$OBJ_AR" ./tools/build_llvm.sh apps/tests/ny-array-llvm-ret/main.nyash -o app_array_ret_llvm >/dev/null || true echo "[llvm-smoke] running app_array_ret_llvm ..." >&2 out_ar=$(./app_array_ret_llvm || true) @@ -103,7 +107,7 @@ if [[ "${NYASH_LLVM_ECHO_SMOKE:-0}" == "1" ]]; then echo "[llvm-smoke] building + linking apps/ny-echo-lite ..." >&2 OBJ_ECHO="$PWD/target/aot_objects/echo_smoke.o" rm -f "$OBJ_ECHO" - NYASH_LLVM_OBJ_OUT="$OBJ_ECHO" LLVM_SYS_181_PREFIX="${_LLVMPREFIX}" LLVM_SYS_180_PREFIX="${_LLVMPREFIX}" "$BIN" --backend llvm apps/tests/ny-echo-lite/main.nyash >/dev/null || true + NYASH_LLVM_OBJ_OUT="$OBJ_ECHO" "$BIN" --backend llvm apps/tests/ny-echo-lite/main.nyash >/dev/null || true NYASH_LLVM_SKIP_EMIT=1 NYASH_LLVM_OBJ_OUT="$OBJ_ECHO" ./tools/build_llvm.sh apps/tests/ny-echo-lite/main.nyash -o app_echo_llvm >/dev/null echo "[llvm-smoke] running app_echo_llvm with stdin ..." >&2 echo "hello-llvm" | ./app_echo_llvm > /tmp/ny_echo_llvm.out || true @@ -128,7 +132,7 @@ if [[ "${NYASH_LLVM_MAP_SMOKE:-0}" == "1" ]] && [[ "${NYASH_DISABLE_PLUGINS:-0}" # Pre-emit object to avoid current lowering gaps, then link OBJ_MAP="$PWD/target/aot_objects/map_smoke.o" rm -f "$OBJ_MAP" - NYASH_LLVM_OBJ_OUT="$OBJ_MAP" LLVM_SYS_181_PREFIX="${_LLVMPREFIX}" LLVM_SYS_180_PREFIX="${_LLVMPREFIX}" "$BIN" --backend llvm apps/tests/ny-map-llvm-smoke/main.nyash >/dev/null || true + NYASH_LLVM_OBJ_OUT="$OBJ_MAP" "$BIN" --backend llvm apps/tests/ny-map-llvm-smoke/main.nyash >/dev/null || true NYASH_LLVM_SKIP_EMIT=1 NYASH_LLVM_OBJ_OUT="$OBJ_MAP" ./tools/build_llvm.sh apps/tests/ny-map-llvm-smoke/main.nyash -o app_map_llvm >/dev/null || true echo "[llvm-smoke] running app_map_llvm ..." >&2 out_map=$(./app_map_llvm || true) @@ -150,7 +154,7 @@ if [[ "${NYASH_LLVM_VINVOKE_SMOKE:-0}" == "1" ]] && [[ "${NYASH_DISABLE_PLUGINS: fi OBJ_V="$PWD/target/aot_objects/vinvoke_smoke.o" rm -f "$OBJ_V" - NYASH_LLVM_OBJ_OUT="$OBJ_V" LLVM_SYS_181_PREFIX="${_LLVMPREFIX}" LLVM_SYS_180_PREFIX="${_LLVMPREFIX}" "$BIN" --backend llvm apps/tests/ny-vinvoke-smoke/main.nyash >/dev/null || true + NYASH_LLVM_OBJ_OUT="$OBJ_V" "$BIN" --backend llvm apps/tests/ny-vinvoke-smoke/main.nyash >/dev/null || true NYASH_LLVM_SKIP_EMIT=1 NYASH_LLVM_OBJ_OUT="$OBJ_V" ./tools/build_llvm.sh apps/tests/ny-vinvoke-smoke/main.nyash -o app_vinvoke_llvm >/dev/null || true echo "[llvm-smoke] running app_vinvoke_llvm ..." >&2 out_v=$(./app_vinvoke_llvm || true) @@ -172,7 +176,7 @@ if [[ "${NYASH_LLVM_VINVOKE_RET_SMOKE:-0}" == "1" ]] && [[ "${NYASH_DISABLE_PLUG fi OBJ_VR="$PWD/target/aot_objects/vinvoke_ret_smoke.o" rm -f "$OBJ_VR" - NYASH_LLVM_OBJ_OUT="$OBJ_VR" LLVM_SYS_181_PREFIX="${_LLVMPREFIX}" LLVM_SYS_180_PREFIX="${_LLVMPREFIX}" "$BIN" --backend llvm apps/tests/ny-vinvoke-llvm-ret/main.nyash >/dev/null || true + NYASH_LLVM_OBJ_OUT="$OBJ_VR" "$BIN" --backend llvm apps/tests/ny-vinvoke-llvm-ret/main.nyash >/dev/null || true NYASH_LLVM_SKIP_EMIT=1 NYASH_LLVM_OBJ_OUT="$OBJ_VR" ./tools/build_llvm.sh apps/tests/ny-vinvoke-llvm-ret/main.nyash -o app_vinvoke_ret_llvm >/dev/null || true echo "[llvm-smoke] running app_vinvoke_ret_llvm ..." >&2 out_vr=$(./app_vinvoke_ret_llvm || true) @@ -194,7 +198,26 @@ if [[ "${NYASH_LLVM_VINVOKE_RET_SMOKE:-0}" == "1" ]] && [[ "${NYASH_DISABLE_PLUG fi OBJ_SIZE="$PWD/target/aot_objects/vinvoke_size_smoke.o" rm -f "$OBJ_SIZE" - NYASH_LLVM_OBJ_OUT="$OBJ_SIZE" LLVM_SYS_181_PREFIX="${_LLVMPREFIX}" LLVM_SYS_180_PREFIX="${_LLVMPREFIX}" "$BIN" --backend llvm apps/tests/ny-vinvoke-llvm-ret-size/main.nyash >/dev/null || true + NYASH_LLVM_OBJ_OUT="$OBJ_SIZE" "$BIN" --backend llvm apps/tests/ny-vinvoke-llvm-ret-size/main.nyash >/dev/null || true + +# --- AOT smoke: plugin return values (CounterBox.get, StringBox.concat) --- +if [[ "${NYASH_LLVM_PLUGIN_RET_SMOKE:-0}" == "1" ]] && [[ "${NYASH_DISABLE_PLUGINS:-0}" != "1" ]]; then + echo "[llvm-smoke] building + linking apps/ny-plugin-ret-llvm-smoke ..." >&2 + OBJ_RET="$PWD/target/aot_objects/plugin_ret_smoke.o" + rm -f "$OBJ_RET" + NYASH_LLVM_OBJ_OUT="$OBJ_RET" "$BIN" --backend llvm apps/tests/ny-plugin-ret-llvm-smoke/main.nyash >/dev/null || true + NYASH_LLVM_SKIP_EMIT=1 NYASH_LLVM_OBJ_OUT="$OBJ_RET" ./tools/build_llvm.sh apps/tests/ny-plugin-ret-llvm-smoke/main.nyash -o app_plugin_ret_llvm >/dev/null || true + echo "[llvm-smoke] running app_plugin_ret_llvm ..." >&2 + out_ret=$(./app_plugin_ret_llvm || true) + echo "[llvm-smoke] output: $out_ret" >&2 + if ! echo "$out_ret" | grep -q "S=abCD" || ! echo "$out_ret" | grep -q "Result: 1"; then + echo "error: plugin-ret-smoke unexpected output: $out_ret" >&2 + exit 1 + fi + echo "[llvm-smoke] OK: plugin return (int/string) smoke passed" >&2 +else + echo "[llvm-smoke] skipping plugin return smoke (set NYASH_LLVM_PLUGIN_RET_SMOKE=1 to enable; requires plugins)" >&2 +fi NYASH_LLVM_SKIP_EMIT=1 NYASH_LLVM_OBJ_OUT="$OBJ_SIZE" ./tools/build_llvm.sh apps/tests/ny-vinvoke-llvm-ret-size/main.nyash -o app_vinvoke_ret_size_llvm >/dev/null || true echo "[llvm-smoke] running app_vinvoke_ret_size_llvm ..." >&2 out_size=$(./app_vinvoke_ret_size_llvm || true)