| 1 | // vtest build: false |
| 2 | |
| 3 | module x64 |
| 4 | |
| 5 | import os |
| 6 | |
| 7 | fn run_x64_backend_runtime_program(name string, source string) string { |
| 8 | tmp_dir := os.join_path(os.vtmp_dir(), 'v_x64_backend_runtime_${name}_${os.getpid()}') |
| 9 | os.mkdir_all(tmp_dir) or { panic(err) } |
| 10 | defer { |
| 11 | os.rmdir_all(tmp_dir) or {} |
| 12 | } |
| 13 | source_path := os.join_path(tmp_dir, '${name}.v') |
| 14 | bin_path := os.join_path(tmp_dir, name) |
| 15 | os.write_file(source_path, source) or { panic(err) } |
| 16 | vexe := os.getenv_opt('VEXE') or { @VEXE } |
| 17 | build := |
| 18 | os.execute('${os.quoted_path(vexe)} -v2 -b x64 ${os.quoted_path(source_path)} -o ${os.quoted_path(bin_path)}') |
| 19 | assert build.exit_code == 0, build.output |
| 20 | run := |
| 21 | os.execute('out=$(${os.quoted_path(bin_path)} 2>&1); code=$?; printf "%s\\n%s" "$code" "$out"') |
| 22 | assert run.exit_code == 0, '${name}: ${run.output}' |
| 23 | lines := run.output.split_into_lines() |
| 24 | assert lines.len >= 1, '${name}: ${run.output}' |
| 25 | assert lines[0] == '0', '${name}: ${run.output}' |
| 26 | return lines[1..].join('\n') |
| 27 | } |
| 28 | |
| 29 | fn run_x64_backend_runtime_compile_error(name string, source string) string { |
| 30 | tmp_dir := os.join_path(os.vtmp_dir(), 'v_x64_backend_runtime_fail_${name}_${os.getpid()}') |
| 31 | os.mkdir_all(tmp_dir) or { panic(err) } |
| 32 | defer { |
| 33 | os.rmdir_all(tmp_dir) or {} |
| 34 | } |
| 35 | source_path := os.join_path(tmp_dir, '${name}.v') |
| 36 | bin_path := os.join_path(tmp_dir, name) |
| 37 | os.write_file(source_path, source) or { panic(err) } |
| 38 | vexe := os.getenv_opt('VEXE') or { @VEXE } |
| 39 | build := |
| 40 | os.execute('${os.quoted_path(vexe)} -v2 -b x64 ${os.quoted_path(source_path)} -o ${os.quoted_path(bin_path)}') |
| 41 | assert build.exit_code != 0, build.output |
| 42 | return build.output |
| 43 | } |
| 44 | |
| 45 | fn run_x64_backend_runtime_program_redirected(name string, source string) (string, string) { |
| 46 | tmp_dir := os.join_path(os.vtmp_dir(), 'v_x64_backend_runtime_redir_${name}_${os.getpid()}') |
| 47 | os.mkdir_all(tmp_dir) or { panic(err) } |
| 48 | defer { |
| 49 | os.rmdir_all(tmp_dir) or {} |
| 50 | } |
| 51 | source_path := os.join_path(tmp_dir, '${name}.v') |
| 52 | bin_path := os.join_path(tmp_dir, name) |
| 53 | stdout_path := os.join_path(tmp_dir, '${name}.out') |
| 54 | stderr_path := os.join_path(tmp_dir, '${name}.err') |
| 55 | os.write_file(source_path, source) or { panic(err) } |
| 56 | vexe := os.getenv_opt('VEXE') or { @VEXE } |
| 57 | build := |
| 58 | os.execute('${os.quoted_path(vexe)} -v2 -b x64 ${os.quoted_path(source_path)} -o ${os.quoted_path(bin_path)}') |
| 59 | assert build.exit_code == 0, build.output |
| 60 | run := |
| 61 | os.execute('${os.quoted_path(bin_path)} > ${os.quoted_path(stdout_path)} 2> ${os.quoted_path(stderr_path)}') |
| 62 | assert run.exit_code == 0, run.output |
| 63 | stdout := os.read_file(stdout_path) or { panic(err) } |
| 64 | stderr := os.read_file(stderr_path) or { panic(err) } |
| 65 | return stdout, stderr |
| 66 | } |
| 67 | |
| 68 | fn test_x64_backend_runtime_hello_world_runs() { |
| 69 | output := run_x64_backend_runtime_program('hello_world', "module main |
| 70 | |
| 71 | fn main() { |
| 72 | println('Hello World!') |
| 73 | } |
| 74 | ") |
| 75 | assert output == 'Hello World!' |
| 76 | } |
| 77 | |
| 78 | fn test_x64_backend_runtime_hello_world_runs_with_stdout_redirected() { |
| 79 | stdout, stderr := run_x64_backend_runtime_program_redirected('hello_world_redir', "module main |
| 80 | |
| 81 | fn main() { |
| 82 | println('Hello World!') |
| 83 | } |
| 84 | ") |
| 85 | assert stdout == 'Hello World!\n' |
| 86 | assert stderr == '' |
| 87 | } |
| 88 | |
| 89 | fn test_x64_backend_runtime_const_init_is_not_skipped() { |
| 90 | output := run_x64_backend_runtime_program('const_string', "module main |
| 91 | |
| 92 | const greeting = 'Const hello' |
| 93 | |
| 94 | fn main() { |
| 95 | println(greeting) |
| 96 | } |
| 97 | ") |
| 98 | assert output == 'Const hello' |
| 99 | } |
| 100 | |
| 101 | fn test_x64_backend_runtime_branch_and_local_mutation_pipeline_correctness() { |
| 102 | output := run_x64_backend_runtime_program('branch_local_mutation', "module main |
| 103 | |
| 104 | fn adjustment(flag bool) int { |
| 105 | if flag { |
| 106 | return 7 |
| 107 | } |
| 108 | return -3 |
| 109 | } |
| 110 | |
| 111 | fn score(a int, b int, flag bool) int { |
| 112 | mut total := a * 10 |
| 113 | total += adjustment(flag) |
| 114 | if total > b { |
| 115 | return total - b |
| 116 | } |
| 117 | return b - total |
| 118 | } |
| 119 | |
| 120 | fn main() { |
| 121 | if score(4, 35, true) == 12 && score(4, 35, false) == 2 { |
| 122 | println('ok') |
| 123 | } else { |
| 124 | println('bad') |
| 125 | } |
| 126 | } |
| 127 | ") |
| 128 | assert output == 'ok' |
| 129 | } |
| 130 | |
| 131 | fn test_x64_backend_runtime_trunc_and_zext_mask_integer_values() { |
| 132 | output := run_x64_backend_runtime_program('int_cast_masks', "module main |
| 133 | |
| 134 | fn narrow_and_widen(x u16) u64 { |
| 135 | y := u8(x) |
| 136 | return u64(y) |
| 137 | } |
| 138 | |
| 139 | fn narrow_u32_to_u16(x u32) u64 { |
| 140 | y := u16(x) |
| 141 | return u64(y) |
| 142 | } |
| 143 | |
| 144 | fn narrow_u64_to_u8(x u64) u64 { |
| 145 | y := u8(x) |
| 146 | return u64(y) |
| 147 | } |
| 148 | |
| 149 | fn sign_extend_i8(x i8) i64 { |
| 150 | return i64(x) |
| 151 | } |
| 152 | |
| 153 | fn sign_extend_i16(x i16) i64 { |
| 154 | return i64(x) |
| 155 | } |
| 156 | |
| 157 | fn main() { |
| 158 | if narrow_and_widen(u16(0x1234)) == 0x34 |
| 159 | && narrow_u32_to_u16(u32(0x12345678)) == 0x5678 |
| 160 | && narrow_u64_to_u8(u64(0x123456789ABCDEFF)) == 0xFF |
| 161 | && sign_extend_i8(i8(-42)) == -42 |
| 162 | && sign_extend_i16(i16(-12345)) == -12345 { |
| 163 | println('ok') |
| 164 | } else { |
| 165 | println('bad') |
| 166 | } |
| 167 | } |
| 168 | ") |
| 169 | assert output == 'ok' |
| 170 | } |
| 171 | |
| 172 | fn test_x64_backend_runtime_signed_memory_loads_extend_values() { |
| 173 | output := run_x64_backend_runtime_program('signed_memory_loads', "module main |
| 174 | |
| 175 | fn load_i8_from_ptr(p &i8) i64 { |
| 176 | return i64(*p) |
| 177 | } |
| 178 | |
| 179 | fn load_i16_from_ptr(p &i16) i64 { |
| 180 | return i64(*p) |
| 181 | } |
| 182 | |
| 183 | fn main() { |
| 184 | mut a := i8(-42) |
| 185 | mut b := i16(-12345) |
| 186 | if load_i8_from_ptr(&a) == -42 && load_i16_from_ptr(&b) == -12345 { |
| 187 | println('ok') |
| 188 | } else { |
| 189 | println('bad') |
| 190 | } |
| 191 | } |
| 192 | ") |
| 193 | assert output == 'ok' |
| 194 | } |
| 195 | |
| 196 | fn test_x64_backend_runtime_raw_copy_tails_do_not_overwrite_sentinel() { |
| 197 | output := run_x64_backend_runtime_program('raw_copy_tails', "module main |
| 198 | |
| 199 | struct Tiny3 { |
| 200 | mut: |
| 201 | a u8 |
| 202 | b u8 |
| 203 | c u8 |
| 204 | } |
| 205 | |
| 206 | struct Wrap3 { |
| 207 | mut: |
| 208 | data Tiny3 |
| 209 | sentinel u8 |
| 210 | } |
| 211 | |
| 212 | struct Tiny5 { |
| 213 | mut: |
| 214 | a u8 |
| 215 | b u8 |
| 216 | c u8 |
| 217 | d u8 |
| 218 | e u8 |
| 219 | } |
| 220 | |
| 221 | struct Wrap5 { |
| 222 | mut: |
| 223 | data Tiny5 |
| 224 | sentinel u8 |
| 225 | } |
| 226 | |
| 227 | struct Tiny6 { |
| 228 | mut: |
| 229 | a u8 |
| 230 | b u8 |
| 231 | c u8 |
| 232 | d u8 |
| 233 | e u8 |
| 234 | f u8 |
| 235 | } |
| 236 | |
| 237 | struct Wrap6 { |
| 238 | mut: |
| 239 | data Tiny6 |
| 240 | sentinel u8 |
| 241 | } |
| 242 | |
| 243 | struct Tiny7 { |
| 244 | mut: |
| 245 | a u8 |
| 246 | b u8 |
| 247 | c u8 |
| 248 | d u8 |
| 249 | e u8 |
| 250 | f u8 |
| 251 | g u8 |
| 252 | } |
| 253 | |
| 254 | struct Wrap7 { |
| 255 | mut: |
| 256 | data Tiny7 |
| 257 | sentinel u8 |
| 258 | } |
| 259 | |
| 260 | fn check3() bool { |
| 261 | src := Tiny3{ |
| 262 | a: 1 |
| 263 | b: 2 |
| 264 | c: 3 |
| 265 | } |
| 266 | mut w := Wrap3{ |
| 267 | sentinel: 0x5A |
| 268 | } |
| 269 | w.data = src |
| 270 | return w.sentinel == 0x5A && w.data.a == 1 && w.data.b == 2 && w.data.c == 3 |
| 271 | } |
| 272 | |
| 273 | fn check5() bool { |
| 274 | src := Tiny5{ |
| 275 | a: 1 |
| 276 | b: 2 |
| 277 | c: 3 |
| 278 | d: 4 |
| 279 | e: 5 |
| 280 | } |
| 281 | mut w := Wrap5{ |
| 282 | sentinel: 0x5A |
| 283 | } |
| 284 | w.data = src |
| 285 | return w.sentinel == 0x5A && w.data.a == 1 && w.data.b == 2 && w.data.c == 3 |
| 286 | && w.data.d == 4 && w.data.e == 5 |
| 287 | } |
| 288 | |
| 289 | fn check6() bool { |
| 290 | src := Tiny6{ |
| 291 | a: 1 |
| 292 | b: 2 |
| 293 | c: 3 |
| 294 | d: 4 |
| 295 | e: 5 |
| 296 | f: 6 |
| 297 | } |
| 298 | mut w := Wrap6{ |
| 299 | sentinel: 0x5A |
| 300 | } |
| 301 | w.data = src |
| 302 | return w.sentinel == 0x5A && w.data.a == 1 && w.data.b == 2 && w.data.c == 3 |
| 303 | && w.data.d == 4 && w.data.e == 5 && w.data.f == 6 |
| 304 | } |
| 305 | |
| 306 | fn check7() bool { |
| 307 | src := Tiny7{ |
| 308 | a: 1 |
| 309 | b: 2 |
| 310 | c: 3 |
| 311 | d: 4 |
| 312 | e: 5 |
| 313 | f: 6 |
| 314 | g: 7 |
| 315 | } |
| 316 | mut w := Wrap7{ |
| 317 | sentinel: 0x5A |
| 318 | } |
| 319 | w.data = src |
| 320 | return w.sentinel == 0x5A && w.data.a == 1 && w.data.b == 2 && w.data.c == 3 |
| 321 | && w.data.d == 4 && w.data.e == 5 && w.data.f == 6 && w.data.g == 7 |
| 322 | } |
| 323 | |
| 324 | fn main() { |
| 325 | if check3() && check5() && check6() && check7() { |
| 326 | println('ok') |
| 327 | } else { |
| 328 | println('bad') |
| 329 | } |
| 330 | } |
| 331 | ") |
| 332 | assert output == 'ok' |
| 333 | } |
| 334 | |
| 335 | fn test_x64_backend_runtime_small_aggregates_pass_by_value_in_registers() { |
| 336 | output := run_x64_backend_runtime_program('small_aggregate_reg_args', "module main |
| 337 | |
| 338 | struct Tiny3 { |
| 339 | a u8 |
| 340 | b u8 |
| 341 | c u8 |
| 342 | } |
| 343 | |
| 344 | struct Tiny5 { |
| 345 | a u8 |
| 346 | b u8 |
| 347 | c u8 |
| 348 | d u8 |
| 349 | e u8 |
| 350 | } |
| 351 | |
| 352 | struct Tiny6 { |
| 353 | a u8 |
| 354 | b u8 |
| 355 | c u8 |
| 356 | d u8 |
| 357 | e u8 |
| 358 | f u8 |
| 359 | } |
| 360 | |
| 361 | struct Tiny7 { |
| 362 | a u8 |
| 363 | b u8 |
| 364 | c u8 |
| 365 | d u8 |
| 366 | e u8 |
| 367 | f u8 |
| 368 | g u8 |
| 369 | } |
| 370 | |
| 371 | fn sum3(t Tiny3) int { |
| 372 | return int(t.a) + int(t.b) + int(t.c) |
| 373 | } |
| 374 | |
| 375 | fn sum5(t Tiny5) int { |
| 376 | return int(t.a) + int(t.b) + int(t.c) + int(t.d) + int(t.e) |
| 377 | } |
| 378 | |
| 379 | fn sum6(t Tiny6) int { |
| 380 | return int(t.a) + int(t.b) + int(t.c) + int(t.d) + int(t.e) + int(t.f) |
| 381 | } |
| 382 | |
| 383 | fn sum7(t Tiny7) int { |
| 384 | return int(t.a) + int(t.b) + int(t.c) + int(t.d) + int(t.e) + int(t.f) + int(t.g) |
| 385 | } |
| 386 | |
| 387 | fn main() { |
| 388 | a := Tiny3{ |
| 389 | a: 1 |
| 390 | b: 2 |
| 391 | c: 3 |
| 392 | } |
| 393 | b := Tiny5{ |
| 394 | a: 1 |
| 395 | b: 2 |
| 396 | c: 3 |
| 397 | d: 4 |
| 398 | e: 5 |
| 399 | } |
| 400 | c := Tiny6{ |
| 401 | a: 1 |
| 402 | b: 2 |
| 403 | c: 3 |
| 404 | d: 4 |
| 405 | e: 5 |
| 406 | f: 6 |
| 407 | } |
| 408 | d := Tiny7{ |
| 409 | a: 1 |
| 410 | b: 2 |
| 411 | c: 3 |
| 412 | d: 4 |
| 413 | e: 5 |
| 414 | f: 6 |
| 415 | g: 7 |
| 416 | } |
| 417 | if sum3(a) == 6 && sum5(b) == 15 && sum6(c) == 21 && sum7(d) == 28 { |
| 418 | println('ok') |
| 419 | } else { |
| 420 | println('bad') |
| 421 | } |
| 422 | } |
| 423 | ") |
| 424 | assert output == 'ok' |
| 425 | } |
| 426 | |
| 427 | fn test_x64_backend_runtime_raw_aggregate_arg_does_not_clobber_rcx_arg() { |
| 428 | output := run_x64_backend_runtime_program('small_aggregate_after_rcx_arg', "module main |
| 429 | |
| 430 | struct Tiny3 { |
| 431 | a u8 |
| 432 | b u8 |
| 433 | c u8 |
| 434 | } |
| 435 | |
| 436 | fn check(a i64, b i64, c i64, marker i64, t Tiny3) i64 { |
| 437 | return marker + i64(t.a) + i64(t.b) + i64(t.c) |
| 438 | } |
| 439 | |
| 440 | fn main() { |
| 441 | t := Tiny3{ |
| 442 | a: 1 |
| 443 | b: 2 |
| 444 | c: 3 |
| 445 | } |
| 446 | if check(10, 20, 30, 1000, t) == 1006 { |
| 447 | println('ok') |
| 448 | } else { |
| 449 | println('bad') |
| 450 | } |
| 451 | } |
| 452 | ") |
| 453 | assert output == 'ok' |
| 454 | } |
| 455 | |
| 456 | fn test_x64_backend_runtime_big17_sret_and_indirect_param_copy_exact_bytes() { |
| 457 | output := run_x64_backend_runtime_program('big17_exact_copies', "module main |
| 458 | |
| 459 | struct Big17 { |
| 460 | mut: |
| 461 | a u8 |
| 462 | b u8 |
| 463 | c u8 |
| 464 | d u8 |
| 465 | e u8 |
| 466 | f u8 |
| 467 | g u8 |
| 468 | h u8 |
| 469 | i u8 |
| 470 | j u8 |
| 471 | k u8 |
| 472 | l u8 |
| 473 | m u8 |
| 474 | n u8 |
| 475 | o u8 |
| 476 | p u8 |
| 477 | q u8 |
| 478 | } |
| 479 | |
| 480 | fn make_big() Big17 { |
| 481 | return Big17{ |
| 482 | a: 1 |
| 483 | b: 2 |
| 484 | c: 3 |
| 485 | d: 4 |
| 486 | e: 5 |
| 487 | f: 6 |
| 488 | g: 7 |
| 489 | h: 8 |
| 490 | i: 9 |
| 491 | j: 10 |
| 492 | k: 11 |
| 493 | l: 12 |
| 494 | m: 13 |
| 495 | n: 14 |
| 496 | o: 15 |
| 497 | p: 16 |
| 498 | q: 17 |
| 499 | } |
| 500 | } |
| 501 | |
| 502 | fn id_big(x Big17) Big17 { |
| 503 | return x |
| 504 | } |
| 505 | |
| 506 | fn sum_big(x Big17) int { |
| 507 | return int(x.a) + int(x.b) + int(x.c) + int(x.d) + int(x.e) + int(x.f) + int(x.g) |
| 508 | + int(x.h) + int(x.i) + int(x.j) + int(x.k) + int(x.l) + int(x.m) + int(x.n) |
| 509 | + int(x.o) + int(x.p) + int(x.q) |
| 510 | } |
| 511 | |
| 512 | fn main() { |
| 513 | x := make_big() |
| 514 | y := id_big(x) |
| 515 | if sum_big(y) == 153 { |
| 516 | println('ok') |
| 517 | } else { |
| 518 | println('bad') |
| 519 | } |
| 520 | } |
| 521 | ") |
| 522 | assert output == 'ok' |
| 523 | } |
| 524 | |
| 525 | fn test_x64_backend_runtime_fixed_array_store_uses_raw_copy() { |
| 526 | output := run_x64_backend_runtime_program('fixed_array_store', "module main |
| 527 | |
| 528 | fn main() { |
| 529 | mut src := [3]u8{} |
| 530 | src[0] = 1 |
| 531 | src[1] = 2 |
| 532 | src[2] = 3 |
| 533 | mut dst := [3]u8{} |
| 534 | dst = src |
| 535 | if dst[0] == 1 && dst[1] == 2 && dst[2] == 3 { |
| 536 | println('ok') |
| 537 | } else { |
| 538 | println('bad') |
| 539 | } |
| 540 | } |
| 541 | ") |
| 542 | assert output == 'ok' |
| 543 | } |
| 544 | |
| 545 | fn test_x64_backend_runtime_heap_sizing_allows_struct_access_past_eight_bytes() { |
| 546 | output := run_x64_backend_runtime_program('heap_struct_size', "module main |
| 547 | |
| 548 | struct Heap17 { |
| 549 | mut: |
| 550 | a u8 |
| 551 | b u8 |
| 552 | c u8 |
| 553 | d u8 |
| 554 | e u8 |
| 555 | f u8 |
| 556 | g u8 |
| 557 | h u8 |
| 558 | i u8 |
| 559 | j u8 |
| 560 | k u8 |
| 561 | l u8 |
| 562 | m u8 |
| 563 | n u8 |
| 564 | o u8 |
| 565 | p u8 |
| 566 | q u8 |
| 567 | } |
| 568 | |
| 569 | fn main() { |
| 570 | mut p := &Heap17{} |
| 571 | p.q = 17 |
| 572 | if p.q == 17 { |
| 573 | println('ok') |
| 574 | } else { |
| 575 | println('bad') |
| 576 | } |
| 577 | } |
| 578 | ") |
| 579 | assert output == 'ok' |
| 580 | } |
| 581 | |
| 582 | fn test_x64_backend_runtime_float_width_casts_convert_values() { |
| 583 | output := run_x64_backend_runtime_program('float_width_casts', "module main |
| 584 | |
| 585 | fn widen(x f32) f64 { |
| 586 | return f64(x) |
| 587 | } |
| 588 | |
| 589 | fn narrow(x f64) f32 { |
| 590 | return f32(x) |
| 591 | } |
| 592 | |
| 593 | fn main() { |
| 594 | a := widen(f32(1.5)) |
| 595 | b := narrow(a + 0.25) |
| 596 | if a > 1.49 && a < 1.51 && b > f32(1.74) && b < f32(1.76) { |
| 597 | println('ok') |
| 598 | } else { |
| 599 | println('bad') |
| 600 | } |
| 601 | } |
| 602 | ") |
| 603 | assert output == 'ok' |
| 604 | } |
| 605 | |
| 606 | fn test_x64_backend_runtime_scalar_float_abi_uses_xmm_registers() { |
| 607 | output := run_x64_backend_runtime_program('scalar_float_abi', "module main |
| 608 | |
| 609 | fn from_f64_u32(x f64) u32 { |
| 610 | return u32(x) |
| 611 | } |
| 612 | |
| 613 | fn id_f64(x f64) f64 { |
| 614 | return x |
| 615 | } |
| 616 | |
| 617 | fn id_f32(x f32) f32 { |
| 618 | return x |
| 619 | } |
| 620 | |
| 621 | fn mixed(a i64, x f64, b i64, y f32, c i64) i64 { |
| 622 | if x > 1.24 && x < 1.26 && y > f32(2.49) && y < f32(2.51) { |
| 623 | return a + b + c |
| 624 | } |
| 625 | return -1 |
| 626 | } |
| 627 | |
| 628 | fn main() { |
| 629 | x := 4000000000.0 |
| 630 | local := u32(x) == u32(4000000000) |
| 631 | low := from_f64_u32(42.0) == u32(42) |
| 632 | high := from_f64_u32(4000000000.0) == u32(4000000000) |
| 633 | ret64 := id_f64(1.25) > 1.24 && id_f64(1.25) < 1.26 |
| 634 | ret32 := id_f32(f32(2.5)) > f32(2.49) && id_f32(f32(2.5)) < f32(2.51) |
| 635 | mixed_ok := mixed(10, 1.25, 20, f32(2.5), 30) == 60 |
| 636 | if local && low && high && ret64 && ret32 && mixed_ok { |
| 637 | println('ok') |
| 638 | } else { |
| 639 | println('bad') |
| 640 | } |
| 641 | } |
| 642 | ") |
| 643 | assert output == 'ok' |
| 644 | } |
| 645 | |
| 646 | fn test_x64_backend_runtime_scalar_float_stack_args_fail_explicitly() { |
| 647 | output := run_x64_backend_runtime_compile_error('float_stack_arg_unsupported', 'module main |
| 648 | |
| 649 | fn many(a f64, b f64, c f64, d f64, e f64, f f64, g f64, h f64, i f64) f64 { |
| 650 | return a + b + c + d + e + f + g + h + i |
| 651 | } |
| 652 | |
| 653 | fn main() { |
| 654 | _ = many(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0) |
| 655 | } |
| 656 | ') |
| 657 | assert output.contains('x64: unsupported backend feature: stack-passed float parameter') |
| 658 | } |
| 659 | |
| 660 | fn test_x64_backend_runtime_float_comparisons_use_numeric_semantics() { |
| 661 | output := run_x64_backend_runtime_program('float_comparisons', "module main |
| 662 | |
| 663 | import os |
| 664 | |
| 665 | fn eq_f64(a f64, b f64) bool { |
| 666 | return a == b |
| 667 | } |
| 668 | |
| 669 | fn ne_f64(a f64, b f64) bool { |
| 670 | return a != b |
| 671 | } |
| 672 | |
| 673 | fn lt_f64(a f64, b f64) bool { |
| 674 | return a < b |
| 675 | } |
| 676 | |
| 677 | fn gt_f64(a f64, b f64) bool { |
| 678 | return a > b |
| 679 | } |
| 680 | |
| 681 | fn le_f64(a f64, b f64) bool { |
| 682 | return a <= b |
| 683 | } |
| 684 | |
| 685 | fn ge_f64(a f64, b f64) bool { |
| 686 | return a >= b |
| 687 | } |
| 688 | |
| 689 | fn le_f32(a f32, b f32) bool { |
| 690 | return a <= b |
| 691 | } |
| 692 | |
| 693 | fn gt_f32(a f32, b f32) bool { |
| 694 | return a > b |
| 695 | } |
| 696 | |
| 697 | fn ge_f32(a f32, b f32) bool { |
| 698 | return a >= b |
| 699 | } |
| 700 | |
| 701 | fn eq_f32(a f32, b f32) bool { |
| 702 | return a == b |
| 703 | } |
| 704 | |
| 705 | fn ne_f32(a f32, b f32) bool { |
| 706 | return a != b |
| 707 | } |
| 708 | |
| 709 | fn lt_f32(a f32, b f32) bool { |
| 710 | return a < b |
| 711 | } |
| 712 | |
| 713 | fn main() { |
| 714 | argc := os.args.len |
| 715 | z := f64(argc - argc) |
| 716 | one := f64(argc) |
| 717 | two := one + one |
| 718 | neg_zero := -z |
| 719 | nan := z / z |
| 720 | ok64 := eq_f64(z, neg_zero) && ne_f64(nan, nan) && lt_f64(-two, -one) |
| 721 | && gt_f64(-one, -two) && le_f64(z, neg_zero) && ge_f64(z, neg_zero) |
| 722 | nan64 := !eq_f64(nan, nan) && ne_f64(nan, nan) && !lt_f64(nan, one) |
| 723 | && !le_f64(nan, one) && !gt_f64(nan, one) && !ge_f64(nan, one) |
| 724 | nanf := f32(nan) |
| 725 | onef := f32(one) |
| 726 | ok32 := eq_f32(f32(z), f32(neg_zero)) && lt_f32(f32(-two), f32(-one)) |
| 727 | nan32 := !eq_f32(nanf, nanf) && ne_f32(nanf, nanf) && !lt_f32(nanf, onef) |
| 728 | && !le_f32(nanf, onef) && !gt_f32(nanf, onef) && !ge_f32(nanf, onef) |
| 729 | if ok64 && nan64 && ok32 && nan32 { |
| 730 | println('ok') |
| 731 | } else { |
| 732 | println('bad') |
| 733 | } |
| 734 | } |
| 735 | ") |
| 736 | assert output == 'ok' |
| 737 | } |
| 738 | |
| 739 | fn test_x64_backend_runtime_unsigned_64_to_float_handles_high_bit() { |
| 740 | output := run_x64_backend_runtime_program('u64_to_float_high_bit', "module main |
| 741 | |
| 742 | fn to_f64(x u64) f64 { |
| 743 | return f64(x) |
| 744 | } |
| 745 | |
| 746 | fn to_f32(x u64) f32 { |
| 747 | return f32(x) |
| 748 | } |
| 749 | |
| 750 | fn main() { |
| 751 | a := to_f64(u64(9223372036854775808)) |
| 752 | b := to_f64(u64(18446744073709551615)) |
| 753 | c := to_f32(u64(9223372036854775808)) |
| 754 | if a == 9223372036854775808.0 && b > 18446744073709550000.0 && c > f32(9223371000000000000.0) { |
| 755 | println('ok') |
| 756 | } else { |
| 757 | println('bad') |
| 758 | } |
| 759 | } |
| 760 | ") |
| 761 | assert output == 'ok' |
| 762 | } |
| 763 | |
| 764 | fn test_x64_backend_runtime_float_to_unsigned_64_handles_high_bit() { |
| 765 | output := run_x64_backend_runtime_program('float_to_u64_high_bit', "module main |
| 766 | |
| 767 | fn from_f64(x f64) u64 { |
| 768 | return u64(x) |
| 769 | } |
| 770 | |
| 771 | fn from_f32(x f32) u64 { |
| 772 | return u64(x) |
| 773 | } |
| 774 | |
| 775 | fn main() { |
| 776 | a := from_f64(9223372036854775808.0) |
| 777 | b := from_f64(9223372036854777856.0) |
| 778 | c := from_f32(f32(9223372036854775808.0)) |
| 779 | if a == u64(9223372036854775808) && b == u64(9223372036854777856) |
| 780 | && c == u64(9223372036854775808) { |
| 781 | println('ok') |
| 782 | } else { |
| 783 | println('bad') |
| 784 | } |
| 785 | } |
| 786 | ") |
| 787 | assert output == 'ok' |
| 788 | } |
| 789 | |
| 790 | fn test_x64_backend_runtime_aggregate_arg_spills_to_stack() { |
| 791 | output := run_x64_backend_runtime_program('aggregate_arg_spill', "module main |
| 792 | |
| 793 | struct Pair { |
| 794 | a i64 |
| 795 | b i64 |
| 796 | } |
| 797 | |
| 798 | fn sum_after_five(a i64, b i64, c i64, d i64, e i64, p Pair) i64 { |
| 799 | return a + b + c + d + e + p.a + p.b |
| 800 | } |
| 801 | |
| 802 | fn main() { |
| 803 | if sum_after_five(1, 2, 3, 4, 5, Pair{6, 7}) == 28 { |
| 804 | println('ok') |
| 805 | } else { |
| 806 | println('bad') |
| 807 | } |
| 808 | } |
| 809 | ") |
| 810 | assert output == 'ok' |
| 811 | } |
| 812 | |
| 813 | fn test_x64_backend_runtime_stack_arg_after_two_chunk_aggregate() { |
| 814 | output := run_x64_backend_runtime_program('stack_arg_after_pair', "module main |
| 815 | |
| 816 | struct Pair { |
| 817 | a i64 |
| 818 | b i64 |
| 819 | } |
| 820 | |
| 821 | fn sum_pair_then_scalars(p Pair, a i64, b i64, c i64, d i64, e i64) i64 { |
| 822 | return p.a + p.b + a + b + c + d + e |
| 823 | } |
| 824 | |
| 825 | fn main() { |
| 826 | if sum_pair_then_scalars(Pair{10, 20}, 1, 2, 3, 4, 5) == 45 { |
| 827 | println('ok') |
| 828 | } else { |
| 829 | println('bad') |
| 830 | } |
| 831 | } |
| 832 | ") |
| 833 | assert output == 'ok' |
| 834 | } |
| 835 | |
| 836 | fn test_x64_backend_runtime_spilled_aggregate_does_not_consume_remaining_register() { |
| 837 | output := run_x64_backend_runtime_program('aggregate_spill_then_reg', "module main |
| 838 | |
| 839 | struct Pair { |
| 840 | a i64 |
| 841 | b i64 |
| 842 | } |
| 843 | |
| 844 | fn sum_spill_then_reg(a i64, b i64, c i64, d i64, e i64, p Pair, z i64) i64 { |
| 845 | return p.a + p.b + z |
| 846 | } |
| 847 | |
| 848 | fn main() { |
| 849 | if sum_spill_then_reg(1, 2, 3, 4, 5, Pair{10, 20}, 6) == 36 { |
| 850 | println('ok') |
| 851 | } else { |
| 852 | println('bad') |
| 853 | } |
| 854 | } |
| 855 | ") |
| 856 | assert output == 'ok' |
| 857 | } |
| 858 | |