module atomic_rt_test @test;
import compiler_rt;

const CInt SEQ_CST = 6;

fn void is_lock_free()
{
	assert(compiler_rt::__atomic_is_lock_free(1, null));
	assert(compiler_rt::__atomic_is_lock_free(2, null));
	assert(compiler_rt::__atomic_is_lock_free(4, null));
	assert(!compiler_rt::__atomic_is_lock_free(16, null));
	assert(!compiler_rt::__atomic_is_lock_free(3, null));
}

fn void load_store_char()
{
	char val, result;
	char src = 0xAB;
	compiler_rt::__atomic_store(1, &val, &src, SEQ_CST);
	compiler_rt::__atomic_load(1, &val, &result, SEQ_CST);
	test::eq(result, 0xAB);
}

fn void load_store_short()
{
	short val, result;
	short src = 0x1234;
	compiler_rt::__atomic_store(2, &val, &src, SEQ_CST);
	compiler_rt::__atomic_load(2, &val, &result, SEQ_CST);
	test::eq(result, 0x1234);
}

fn void load_store_int()
{
	int val, result;
	int src = 12345;
	compiler_rt::__atomic_store(4, &val, &src, SEQ_CST);
	compiler_rt::__atomic_load(4, &val, &result, SEQ_CST);
	test::eq(result, 12345);
}

fn void load_store_long()
{
	$if iptr::size >= 8:
		long val, result;
		long src = 0x123456789ABCDEF0;
		compiler_rt::__atomic_store(8, &val, &src, SEQ_CST);
		compiler_rt::__atomic_load(8, &val, &result, SEQ_CST);
		assert(result == 0x123456789ABCDEF0);
	$endif
}

fn void load_store_large()
{
	// 16-byte value exercises the lock-based fallback path.
	int[4] val;
	int[4] src = { 10, 20, 30, 40 };
	int[4] result;
	compiler_rt::__atomic_store(16, &val, &src, SEQ_CST);
	compiler_rt::__atomic_load(16, &val, &result, SEQ_CST);
	test::eq(result, (int[4]){ 10, 20, 30, 40});
}

fn void exchange_int()
{
	int val = 100;
	int new_val = 200;
	int old;
	compiler_rt::__atomic_exchange(4, &val, &new_val, &old, SEQ_CST);
	assert(old == 100, "expected old 100, got %d", old);
	int loaded;
	compiler_rt::__atomic_load(4, &val, &loaded, SEQ_CST);
	assert(loaded == 200, "expected 200, got %d", loaded);
}

fn void exchange_large()
{
	// Lock-based fallback path.
	int[4] val = { 1, 2, 3, 4 };
	int[4] new_val = { 5, 6, 7, 8 };
	int[4] old;
	compiler_rt::__atomic_exchange(16, &val, &new_val, &old, SEQ_CST);
	test::eq(old,  (int[4]){ 1, 2, 3, 4 });
	int[4] loaded;
	compiler_rt::__atomic_load(16, &val, &loaded, SEQ_CST);
	test::eq(loaded, (int[4]){ 5, 6, 7, 8 });
}

fn void compare_exchange_success()
{
	int val = 42;
	int expected = 42;
	int desired = 99;
	CInt result = compiler_rt::__atomic_compare_exchange(4, &val, &expected, &desired, 0, SEQ_CST, SEQ_CST);
	assert(result == 1, "CAS should have succeeded");
	int loaded;
	compiler_rt::__atomic_load(4, &val, &loaded, SEQ_CST);
	assert(loaded == 99, "expected 99, got %d", loaded);
}

fn void compare_exchange_failure()
{
	int val = 42;
	int expected = 10; // Wrong expected → should fail
	int desired = 99;
	CInt result = compiler_rt::__atomic_compare_exchange(4, &val, &expected, &desired, 0, SEQ_CST, SEQ_CST);
	test::eq(result, 0);
	test::eq(expected, 42);
	int loaded;
	compiler_rt::__atomic_load(4, &val, &loaded, SEQ_CST);
	test::eq(loaded, 42);
}

fn void compare_exchange_large_success()
{
	// Lock fallback path.
	int[4] val = { 1, 2, 3, 4 };
	int[4] expected = { 1, 2, 3, 4 };
	int[4] desired = { 5, 6, 7, 8 };
	CInt result = compiler_rt::__atomic_compare_exchange(16, &val, &expected, &desired, 0, SEQ_CST, SEQ_CST);
	assert(result == 1, "CAS should have succeeded");
	int[4] loaded;
	compiler_rt::__atomic_load(16, &val, &loaded, SEQ_CST);
	test::eq(loaded, (int[4]){ 5, 6, 7, 8 });
}

fn void compare_exchange_large_failure()
{
	// Lock fallback path, mismatch.
	int[4] val = { 1, 2, 3, 4 };
	int[4] expected = { 1, 2, 99, 4 }; // Mismatch at [2]
	int[4] desired = { 5, 6, 7, 8 };
	CInt result = compiler_rt::__atomic_compare_exchange(16, &val, &expected, &desired, 0, SEQ_CST, SEQ_CST);
	assert(result == 0, "CAS should have failed");
	// expected should now contain the actual value
	test::eq(expected, (int[4]){ 1, 2, 3, 4 });
}