//@ revisions: wasm32-unknown wasm64-unknown wasm32-wasip1
//@ add-core-stubs
//@ assembly-output: emit-asm
//@ [wasm32-unknown] compile-flags: --target wasm32-unknown-unknown
//@ [wasm64-unknown] compile-flags: --target wasm64-unknown-unknown
//@ [wasm32-wasip1] compile-flags: --target wasm32-wasip1
//@ [wasm32-unknown] needs-llvm-components: webassembly
//@ [wasm64-unknown] needs-llvm-components: webassembly
//@ [wasm32-wasip1] needs-llvm-components: webassembly

#![crate_type = "lib"]
// FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity
#![feature(rustc_attrs)]
#![feature(no_core, asm_experimental_arch, f128, linkage, fn_align)]
#![no_core]

extern crate minicore;
use minicore::*;

// CHECK: .section  .text.nop,"",@
// CHECK: .globl nop
// CHECK-LABEL: nop:
// CHECK: .functype nop () -> ()
// CHECK-NOT: .size
// CHECK: end_function
#[no_mangle]
#[unsafe(naked)]
extern "C" fn nop() {
    naked_asm!("nop")
}

// CHECK: .section  .text.weak_aligned_nop,"",@
// CHECK: .weak weak_aligned_nop
// CHECK-LABEL: nop:
// CHECK: .functype weak_aligned_nop () -> ()
// CHECK-NOT: .size
// CHECK: end_function
#[no_mangle]
#[unsafe(naked)]
#[linkage = "weak"]
// wasm functions cannot be aligned, so this has no effect
#[rustc_align(32)]
extern "C" fn weak_aligned_nop() {
    naked_asm!("nop")
}

// CHECK-LABEL: fn_i8_i8:
// CHECK-NEXT: .functype fn_i8_i8 (i32) -> (i32)
//
// CHECK-NEXT: local.get 0
// CHECK-NEXT: local.get 0
// CHECK-NEXT: i32.mul
//
// CHECK-NEXT: end_function
#[no_mangle]
#[unsafe(naked)]
extern "C" fn fn_i8_i8(num: i8) -> i8 {
    naked_asm!("local.get 0", "local.get 0", "i32.mul")
}

// CHECK-LABEL: fn_i8_i8_i8:
// CHECK: .functype fn_i8_i8_i8 (i32, i32) -> (i32)
#[no_mangle]
#[unsafe(naked)]
extern "C" fn fn_i8_i8_i8(a: i8, b: i8) -> i8 {
    naked_asm!("local.get 1", "local.get 0", "i32.mul")
}

// CHECK-LABEL: fn_unit_i8:
// CHECK: .functype fn_unit_i8 () -> (i32)
#[no_mangle]
#[unsafe(naked)]
extern "C" fn fn_unit_i8() -> i8 {
    naked_asm!("i32.const 42")
}

// CHECK-LABEL: fn_i8_unit:
// CHECK: .functype fn_i8_unit (i32) -> ()
#[no_mangle]
#[unsafe(naked)]
extern "C" fn fn_i8_unit(_: i8) {
    naked_asm!("nop")
}

// CHECK-LABEL: fn_i32_i32:
// CHECK: .functype fn_i32_i32 (i32) -> (i32)
#[no_mangle]
#[unsafe(naked)]
extern "C" fn fn_i32_i32(num: i32) -> i32 {
    naked_asm!("local.get 0", "local.get 0", "i32.mul")
}

// CHECK-LABEL: fn_i64_i64:
// CHECK: .functype fn_i64_i64 (i64) -> (i64)
#[no_mangle]
#[unsafe(naked)]
extern "C" fn fn_i64_i64(num: i64) -> i64 {
    naked_asm!("local.get 0", "local.get 0", "i64.mul")
}

// CHECK-LABEL: fn_i128_i128:
// wasm32-unknown: .functype fn_i128_i128 (i32, i64, i64) -> ()
// wasm32-wasip1: .functype fn_i128_i128 (i32, i64, i64) -> ()
// wasm64-unknown: .functype fn_i128_i128 (i64, i64, i64) -> ()
#[allow(improper_ctypes_definitions)]
#[no_mangle]
#[unsafe(naked)]
extern "C" fn fn_i128_i128(num: i128) -> i128 {
    naked_asm!(
        "local.get       0",
        "local.get       2",
        "i64.store       8",
        "local.get       0",
        "local.get       1",
        "i64.store       0",
    )
}

// CHECK-LABEL: fn_f128_f128:
// wasm32-unknown: .functype fn_f128_f128 (i32, i64, i64) -> ()
// wasm32-wasip1: .functype fn_f128_f128 (i32, i64, i64) -> ()
// wasm64-unknown: .functype fn_f128_f128 (i64, i64, i64) -> ()
#[no_mangle]
#[unsafe(naked)]
extern "C" fn fn_f128_f128(num: f128) -> f128 {
    naked_asm!(
        "local.get       0",
        "local.get       2",
        "i64.store       8",
        "local.get       0",
        "local.get       1",
        "i64.store       0",
    )
}

#[repr(C)]
struct Compound {
    a: u16,
    b: i64,
}

// CHECK-LABEL: fn_compound_compound:
// wasm32-unknown: .functype fn_compound_compound (i32, i32) -> ()
// wasm32-wasip1: .functype fn_compound_compound (i32, i32) -> ()
// wasm64-unknown: .functype fn_compound_compound (i64, i64) -> ()
#[no_mangle]
#[unsafe(naked)]
extern "C" fn fn_compound_compound(_: Compound) -> Compound {
    // this is the wasm32-wasip1 assembly
    naked_asm!(
        "local.get       0",
        "local.get       1",
        "i64.load        8",
        "i64.store       8",
        "local.get       0",
        "local.get       1",
        "i32.load16_u    0",
        "i32.store16     0",
    )
}

#[repr(C)]
struct WrapperI32(i32);

// CHECK-LABEL: fn_wrapperi32_wrapperi32:
// CHECK: .functype fn_wrapperi32_wrapperi32 (i32) -> (i32)
#[no_mangle]
#[unsafe(naked)]
extern "C" fn fn_wrapperi32_wrapperi32(_: WrapperI32) -> WrapperI32 {
    naked_asm!("local.get       0")
}

#[repr(C)]
struct WrapperI64(i64);

// CHECK-LABEL: fn_wrapperi64_wrapperi64:
// CHECK: .functype fn_wrapperi64_wrapperi64 (i64) -> (i64)
#[no_mangle]
#[unsafe(naked)]
extern "C" fn fn_wrapperi64_wrapperi64(_: WrapperI64) -> WrapperI64 {
    naked_asm!("local.get       0")
}

#[repr(C)]
struct WrapperF32(f32);

// CHECK-LABEL: fn_wrapperf32_wrapperf32:
// CHECK: .functype fn_wrapperf32_wrapperf32 (f32) -> (f32)
#[no_mangle]
#[unsafe(naked)]
extern "C" fn fn_wrapperf32_wrapperf32(_: WrapperF32) -> WrapperF32 {
    naked_asm!("local.get       0")
}

#[repr(C)]
struct WrapperF64(f64);

// CHECK-LABEL: fn_wrapperf64_wrapperf64:
// CHECK: .functype fn_wrapperf64_wrapperf64 (f64) -> (f64)
#[no_mangle]
#[unsafe(naked)]
extern "C" fn fn_wrapperf64_wrapperf64(_: WrapperF64) -> WrapperF64 {
    naked_asm!("local.get       0")
}
