| ... | @@ -43,6 +43,10 @@ const WValue = union(enum) { | ... | @@ -43,6 +43,10 @@ const WValue = union(enum) { |
| 43 | imm32: u32, | 43 | imm32: u32, |
| 44 | /// An immediate 64bit value | 44 | /// An immediate 64bit value |
| 45 | imm64: u64, | 45 | imm64: u64, |
| | 46 | /// Index into the list of simd128 immediates. This `WValue` is |
| | 47 | /// only possible in very rare cases, therefore it would be |
| | 48 | /// a waste of memory to store the value in a 128 bit integer. |
| | 49 | imm128: u32, |
| 46 | /// A constant 32bit float value | 50 | /// A constant 32bit float value |
| 47 | float32: f32, | 51 | float32: f32, |
| 48 | /// A constant 64bit float value | 52 | /// A constant 64bit float value |
| ... | @@ -116,6 +120,7 @@ const WValue = union(enum) { | ... | @@ -116,6 +120,7 @@ const WValue = union(enum) { |
| 116 | .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return, | 120 | .i64 => gen.free_locals_i64.append(gen.gpa, local_value) catch return, |
| 117 | .f32 => gen.free_locals_f32.append(gen.gpa, local_value) catch return, | 121 | .f32 => gen.free_locals_f32.append(gen.gpa, local_value) catch return, |
| 118 | .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return, | 122 | .f64 => gen.free_locals_f64.append(gen.gpa, local_value) catch return, |
| | 123 | .v128 => gen.free_locals_v128.append(gen.gpa, local_value) catch return, |
| 119 | } | 124 | } |
| 120 | value.* = undefined; | 125 | value.* = undefined; |
| 121 | } | 126 | } |
| ... | @@ -258,18 +263,18 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -258,18 +263,18 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 258 | 8 => switch (args.valtype1.?) { | 263 | 8 => switch (args.valtype1.?) { |
| 259 | .i32 => if (args.signedness.? == .signed) return .i32_load8_s else return .i32_load8_u, | 264 | .i32 => if (args.signedness.? == .signed) return .i32_load8_s else return .i32_load8_u, |
| 260 | .i64 => if (args.signedness.? == .signed) return .i64_load8_s else return .i64_load8_u, | 265 | .i64 => if (args.signedness.? == .signed) return .i64_load8_s else return .i64_load8_u, |
| 261 | .f32, .f64 => unreachable, | 266 | .f32, .f64, .v128 => unreachable, |
| 262 | }, | 267 | }, |
| 263 | 16 => switch (args.valtype1.?) { | 268 | 16 => switch (args.valtype1.?) { |
| 264 | .i32 => if (args.signedness.? == .signed) return .i32_load16_s else return .i32_load16_u, | 269 | .i32 => if (args.signedness.? == .signed) return .i32_load16_s else return .i32_load16_u, |
| 265 | .i64 => if (args.signedness.? == .signed) return .i64_load16_s else return .i64_load16_u, | 270 | .i64 => if (args.signedness.? == .signed) return .i64_load16_s else return .i64_load16_u, |
| 266 | .f32, .f64 => unreachable, | 271 | .f32, .f64, .v128 => unreachable, |
| 267 | }, | 272 | }, |
| 268 | 32 => switch (args.valtype1.?) { | 273 | 32 => switch (args.valtype1.?) { |
| 269 | .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u, | 274 | .i64 => if (args.signedness.? == .signed) return .i64_load32_s else return .i64_load32_u, |
| 270 | .i32 => return .i32_load, | 275 | .i32 => return .i32_load, |
| 271 | .f32 => return .f32_load, | 276 | .f32 => return .f32_load, |
| 272 | .f64 => unreachable, | 277 | .f64, .v128 => unreachable, |
| 273 | }, | 278 | }, |
| 274 | 64 => switch (args.valtype1.?) { | 279 | 64 => switch (args.valtype1.?) { |
| 275 | .i64 => return .i64_load, | 280 | .i64 => return .i64_load, |
| ... | @@ -282,24 +287,25 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -282,24 +287,25 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 282 | .i64 => return .i64_load, | 287 | .i64 => return .i64_load, |
| 283 | .f32 => return .f32_load, | 288 | .f32 => return .f32_load, |
| 284 | .f64 => return .f64_load, | 289 | .f64 => return .f64_load, |
| | 290 | .v128 => unreachable, // handled independently |
| 285 | }, | 291 | }, |
| 286 | .store => if (args.width) |width| { | 292 | .store => if (args.width) |width| { |
| 287 | switch (width) { | 293 | switch (width) { |
| 288 | 8 => switch (args.valtype1.?) { | 294 | 8 => switch (args.valtype1.?) { |
| 289 | .i32 => return .i32_store8, | 295 | .i32 => return .i32_store8, |
| 290 | .i64 => return .i64_store8, | 296 | .i64 => return .i64_store8, |
| 291 | .f32, .f64 => unreachable, | 297 | .f32, .f64, .v128 => unreachable, |
| 292 | }, | 298 | }, |
| 293 | 16 => switch (args.valtype1.?) { | 299 | 16 => switch (args.valtype1.?) { |
| 294 | .i32 => return .i32_store16, | 300 | .i32 => return .i32_store16, |
| 295 | .i64 => return .i64_store16, | 301 | .i64 => return .i64_store16, |
| 296 | .f32, .f64 => unreachable, | 302 | .f32, .f64, .v128 => unreachable, |
| 297 | }, | 303 | }, |
| 298 | 32 => switch (args.valtype1.?) { | 304 | 32 => switch (args.valtype1.?) { |
| 299 | .i64 => return .i64_store32, | 305 | .i64 => return .i64_store32, |
| 300 | .i32 => return .i32_store, | 306 | .i32 => return .i32_store, |
| 301 | .f32 => return .f32_store, | 307 | .f32 => return .f32_store, |
| 302 | .f64 => unreachable, | 308 | .f64, .v128 => unreachable, |
| 303 | }, | 309 | }, |
| 304 | 64 => switch (args.valtype1.?) { | 310 | 64 => switch (args.valtype1.?) { |
| 305 | .i64 => return .i64_store, | 311 | .i64 => return .i64_store, |
| ... | @@ -314,6 +320,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -314,6 +320,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 314 | .i64 => return .i64_store, | 320 | .i64 => return .i64_store, |
| 315 | .f32 => return .f32_store, | 321 | .f32 => return .f32_store, |
| 316 | .f64 => return .f64_store, | 322 | .f64 => return .f64_store, |
| | 323 | .v128 => unreachable, // handled independently |
| 317 | } | 324 | } |
| 318 | }, | 325 | }, |
| 319 | | 326 | |
| ... | @@ -325,24 +332,27 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -325,24 +332,27 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 325 | .i64 => return .i64_const, | 332 | .i64 => return .i64_const, |
| 326 | .f32 => return .f32_const, | 333 | .f32 => return .f32_const, |
| 327 | .f64 => return .f64_const, | 334 | .f64 => return .f64_const, |
| | 335 | .v128 => unreachable, // handled independently |
| 328 | }, | 336 | }, |
| 329 | | 337 | |
| 330 | .eqz => switch (args.valtype1.?) { | 338 | .eqz => switch (args.valtype1.?) { |
| 331 | .i32 => return .i32_eqz, | 339 | .i32 => return .i32_eqz, |
| 332 | .i64 => return .i64_eqz, | 340 | .i64 => return .i64_eqz, |
| 333 | .f32, .f64 => unreachable, | 341 | .f32, .f64, .v128 => unreachable, |
| 334 | }, | 342 | }, |
| 335 | .eq => switch (args.valtype1.?) { | 343 | .eq => switch (args.valtype1.?) { |
| 336 | .i32 => return .i32_eq, | 344 | .i32 => return .i32_eq, |
| 337 | .i64 => return .i64_eq, | 345 | .i64 => return .i64_eq, |
| 338 | .f32 => return .f32_eq, | 346 | .f32 => return .f32_eq, |
| 339 | .f64 => return .f64_eq, | 347 | .f64 => return .f64_eq, |
| | 348 | .v128 => unreachable, // handled independently |
| 340 | }, | 349 | }, |
| 341 | .ne => switch (args.valtype1.?) { | 350 | .ne => switch (args.valtype1.?) { |
| 342 | .i32 => return .i32_ne, | 351 | .i32 => return .i32_ne, |
| 343 | .i64 => return .i64_ne, | 352 | .i64 => return .i64_ne, |
| 344 | .f32 => return .f32_ne, | 353 | .f32 => return .f32_ne, |
| 345 | .f64 => return .f64_ne, | 354 | .f64 => return .f64_ne, |
| | 355 | .v128 => unreachable, // handled independently |
| 346 | }, | 356 | }, |
| 347 | | 357 | |
| 348 | .lt => switch (args.valtype1.?) { | 358 | .lt => switch (args.valtype1.?) { |
| ... | @@ -350,40 +360,47 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -350,40 +360,47 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 350 | .i64 => if (args.signedness.? == .signed) return .i64_lt_s else return .i64_lt_u, | 360 | .i64 => if (args.signedness.? == .signed) return .i64_lt_s else return .i64_lt_u, |
| 351 | .f32 => return .f32_lt, | 361 | .f32 => return .f32_lt, |
| 352 | .f64 => return .f64_lt, | 362 | .f64 => return .f64_lt, |
| | 363 | .v128 => unreachable, // handled independently |
| 353 | }, | 364 | }, |
| 354 | .gt => switch (args.valtype1.?) { | 365 | .gt => switch (args.valtype1.?) { |
| 355 | .i32 => if (args.signedness.? == .signed) return .i32_gt_s else return .i32_gt_u, | 366 | .i32 => if (args.signedness.? == .signed) return .i32_gt_s else return .i32_gt_u, |
| 356 | .i64 => if (args.signedness.? == .signed) return .i64_gt_s else return .i64_gt_u, | 367 | .i64 => if (args.signedness.? == .signed) return .i64_gt_s else return .i64_gt_u, |
| 357 | .f32 => return .f32_gt, | 368 | .f32 => return .f32_gt, |
| 358 | .f64 => return .f64_gt, | 369 | .f64 => return .f64_gt, |
| | 370 | .v128 => unreachable, // handled independently |
| 359 | }, | 371 | }, |
| 360 | .le => switch (args.valtype1.?) { | 372 | .le => switch (args.valtype1.?) { |
| 361 | .i32 => if (args.signedness.? == .signed) return .i32_le_s else return .i32_le_u, | 373 | .i32 => if (args.signedness.? == .signed) return .i32_le_s else return .i32_le_u, |
| 362 | .i64 => if (args.signedness.? == .signed) return .i64_le_s else return .i64_le_u, | 374 | .i64 => if (args.signedness.? == .signed) return .i64_le_s else return .i64_le_u, |
| 363 | .f32 => return .f32_le, | 375 | .f32 => return .f32_le, |
| 364 | .f64 => return .f64_le, | 376 | .f64 => return .f64_le, |
| | 377 | .v128 => unreachable, // handled independently |
| 365 | }, | 378 | }, |
| 366 | .ge => switch (args.valtype1.?) { | 379 | .ge => switch (args.valtype1.?) { |
| 367 | .i32 => if (args.signedness.? == .signed) return .i32_ge_s else return .i32_ge_u, | 380 | .i32 => if (args.signedness.? == .signed) return .i32_ge_s else return .i32_ge_u, |
| 368 | .i64 => if (args.signedness.? == .signed) return .i64_ge_s else return .i64_ge_u, | 381 | .i64 => if (args.signedness.? == .signed) return .i64_ge_s else return .i64_ge_u, |
| 369 | .f32 => return .f32_ge, | 382 | .f32 => return .f32_ge, |
| 370 | .f64 => return .f64_ge, | 383 | .f64 => return .f64_ge, |
| | 384 | .v128 => unreachable, // handled independently |
| 371 | }, | 385 | }, |
| 372 | | 386 | |
| 373 | .clz => switch (args.valtype1.?) { | 387 | .clz => switch (args.valtype1.?) { |
| 374 | .i32 => return .i32_clz, | 388 | .i32 => return .i32_clz, |
| 375 | .i64 => return .i64_clz, | 389 | .i64 => return .i64_clz, |
| 376 | .f32, .f64 => unreachable, | 390 | .f32, .f64 => unreachable, |
| | 391 | .v128 => unreachable, // handled independently |
| 377 | }, | 392 | }, |
| 378 | .ctz => switch (args.valtype1.?) { | 393 | .ctz => switch (args.valtype1.?) { |
| 379 | .i32 => return .i32_ctz, | 394 | .i32 => return .i32_ctz, |
| 380 | .i64 => return .i64_ctz, | 395 | .i64 => return .i64_ctz, |
| 381 | .f32, .f64 => unreachable, | 396 | .f32, .f64 => unreachable, |
| | 397 | .v128 => unreachable, // handled independently |
| 382 | }, | 398 | }, |
| 383 | .popcnt => switch (args.valtype1.?) { | 399 | .popcnt => switch (args.valtype1.?) { |
| 384 | .i32 => return .i32_popcnt, | 400 | .i32 => return .i32_popcnt, |
| 385 | .i64 => return .i64_popcnt, | 401 | .i64 => return .i64_popcnt, |
| 386 | .f32, .f64 => unreachable, | 402 | .f32, .f64 => unreachable, |
| | 403 | .v128 => unreachable, // handled independently |
| 387 | }, | 404 | }, |
| 388 | | 405 | |
| 389 | .add => switch (args.valtype1.?) { | 406 | .add => switch (args.valtype1.?) { |
| ... | @@ -391,18 +408,21 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -391,18 +408,21 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 391 | .i64 => return .i64_add, | 408 | .i64 => return .i64_add, |
| 392 | .f32 => return .f32_add, | 409 | .f32 => return .f32_add, |
| 393 | .f64 => return .f64_add, | 410 | .f64 => return .f64_add, |
| | 411 | .v128 => unreachable, // handled independently |
| 394 | }, | 412 | }, |
| 395 | .sub => switch (args.valtype1.?) { | 413 | .sub => switch (args.valtype1.?) { |
| 396 | .i32 => return .i32_sub, | 414 | .i32 => return .i32_sub, |
| 397 | .i64 => return .i64_sub, | 415 | .i64 => return .i64_sub, |
| 398 | .f32 => return .f32_sub, | 416 | .f32 => return .f32_sub, |
| 399 | .f64 => return .f64_sub, | 417 | .f64 => return .f64_sub, |
| | 418 | .v128 => unreachable, // handled independently |
| 400 | }, | 419 | }, |
| 401 | .mul => switch (args.valtype1.?) { | 420 | .mul => switch (args.valtype1.?) { |
| 402 | .i32 => return .i32_mul, | 421 | .i32 => return .i32_mul, |
| 403 | .i64 => return .i64_mul, | 422 | .i64 => return .i64_mul, |
| 404 | .f32 => return .f32_mul, | 423 | .f32 => return .f32_mul, |
| 405 | .f64 => return .f64_mul, | 424 | .f64 => return .f64_mul, |
| | 425 | .v128 => unreachable, // handled independently |
| 406 | }, | 426 | }, |
| 407 | | 427 | |
| 408 | .div => switch (args.valtype1.?) { | 428 | .div => switch (args.valtype1.?) { |
| ... | @@ -410,71 +430,84 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -410,71 +430,84 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 410 | .i64 => if (args.signedness.? == .signed) return .i64_div_s else return .i64_div_u, | 430 | .i64 => if (args.signedness.? == .signed) return .i64_div_s else return .i64_div_u, |
| 411 | .f32 => return .f32_div, | 431 | .f32 => return .f32_div, |
| 412 | .f64 => return .f64_div, | 432 | .f64 => return .f64_div, |
| | 433 | .v128 => unreachable, // handled independently |
| 413 | }, | 434 | }, |
| 414 | .rem => switch (args.valtype1.?) { | 435 | .rem => switch (args.valtype1.?) { |
| 415 | .i32 => if (args.signedness.? == .signed) return .i32_rem_s else return .i32_rem_u, | 436 | .i32 => if (args.signedness.? == .signed) return .i32_rem_s else return .i32_rem_u, |
| 416 | .i64 => if (args.signedness.? == .signed) return .i64_rem_s else return .i64_rem_u, | 437 | .i64 => if (args.signedness.? == .signed) return .i64_rem_s else return .i64_rem_u, |
| 417 | .f32, .f64 => unreachable, | 438 | .f32, .f64 => unreachable, |
| | 439 | .v128 => unreachable, // handled independently |
| 418 | }, | 440 | }, |
| 419 | | 441 | |
| 420 | .@"and" => switch (args.valtype1.?) { | 442 | .@"and" => switch (args.valtype1.?) { |
| 421 | .i32 => return .i32_and, | 443 | .i32 => return .i32_and, |
| 422 | .i64 => return .i64_and, | 444 | .i64 => return .i64_and, |
| 423 | .f32, .f64 => unreachable, | 445 | .f32, .f64 => unreachable, |
| | 446 | .v128 => unreachable, // handled independently |
| 424 | }, | 447 | }, |
| 425 | .@"or" => switch (args.valtype1.?) { | 448 | .@"or" => switch (args.valtype1.?) { |
| 426 | .i32 => return .i32_or, | 449 | .i32 => return .i32_or, |
| 427 | .i64 => return .i64_or, | 450 | .i64 => return .i64_or, |
| 428 | .f32, .f64 => unreachable, | 451 | .f32, .f64 => unreachable, |
| | 452 | .v128 => unreachable, // handled independently |
| 429 | }, | 453 | }, |
| 430 | .xor => switch (args.valtype1.?) { | 454 | .xor => switch (args.valtype1.?) { |
| 431 | .i32 => return .i32_xor, | 455 | .i32 => return .i32_xor, |
| 432 | .i64 => return .i64_xor, | 456 | .i64 => return .i64_xor, |
| 433 | .f32, .f64 => unreachable, | 457 | .f32, .f64 => unreachable, |
| | 458 | .v128 => unreachable, // handled independently |
| 434 | }, | 459 | }, |
| 435 | | 460 | |
| 436 | .shl => switch (args.valtype1.?) { | 461 | .shl => switch (args.valtype1.?) { |
| 437 | .i32 => return .i32_shl, | 462 | .i32 => return .i32_shl, |
| 438 | .i64 => return .i64_shl, | 463 | .i64 => return .i64_shl, |
| 439 | .f32, .f64 => unreachable, | 464 | .f32, .f64 => unreachable, |
| | 465 | .v128 => unreachable, // handled independently |
| 440 | }, | 466 | }, |
| 441 | .shr => switch (args.valtype1.?) { | 467 | .shr => switch (args.valtype1.?) { |
| 442 | .i32 => if (args.signedness.? == .signed) return .i32_shr_s else return .i32_shr_u, | 468 | .i32 => if (args.signedness.? == .signed) return .i32_shr_s else return .i32_shr_u, |
| 443 | .i64 => if (args.signedness.? == .signed) return .i64_shr_s else return .i64_shr_u, | 469 | .i64 => if (args.signedness.? == .signed) return .i64_shr_s else return .i64_shr_u, |
| 444 | .f32, .f64 => unreachable, | 470 | .f32, .f64 => unreachable, |
| | 471 | .v128 => unreachable, // handled independently |
| 445 | }, | 472 | }, |
| 446 | .rotl => switch (args.valtype1.?) { | 473 | .rotl => switch (args.valtype1.?) { |
| 447 | .i32 => return .i32_rotl, | 474 | .i32 => return .i32_rotl, |
| 448 | .i64 => return .i64_rotl, | 475 | .i64 => return .i64_rotl, |
| 449 | .f32, .f64 => unreachable, | 476 | .f32, .f64 => unreachable, |
| | 477 | .v128 => unreachable, // handled independently |
| 450 | }, | 478 | }, |
| 451 | .rotr => switch (args.valtype1.?) { | 479 | .rotr => switch (args.valtype1.?) { |
| 452 | .i32 => return .i32_rotr, | 480 | .i32 => return .i32_rotr, |
| 453 | .i64 => return .i64_rotr, | 481 | .i64 => return .i64_rotr, |
| 454 | .f32, .f64 => unreachable, | 482 | .f32, .f64 => unreachable, |
| | 483 | .v128 => unreachable, // handled independently |
| 455 | }, | 484 | }, |
| 456 | | 485 | |
| 457 | .abs => switch (args.valtype1.?) { | 486 | .abs => switch (args.valtype1.?) { |
| 458 | .i32, .i64 => unreachable, | 487 | .i32, .i64 => unreachable, |
| 459 | .f32 => return .f32_abs, | 488 | .f32 => return .f32_abs, |
| 460 | .f64 => return .f64_abs, | 489 | .f64 => return .f64_abs, |
| | 490 | .v128 => unreachable, // handled independently |
| 461 | }, | 491 | }, |
| 462 | .neg => switch (args.valtype1.?) { | 492 | .neg => switch (args.valtype1.?) { |
| 463 | .i32, .i64 => unreachable, | 493 | .i32, .i64 => unreachable, |
| 464 | .f32 => return .f32_neg, | 494 | .f32 => return .f32_neg, |
| 465 | .f64 => return .f64_neg, | 495 | .f64 => return .f64_neg, |
| | 496 | .v128 => unreachable, // handled independently |
| 466 | }, | 497 | }, |
| 467 | .ceil => switch (args.valtype1.?) { | 498 | .ceil => switch (args.valtype1.?) { |
| 468 | .i64 => unreachable, | 499 | .i64 => unreachable, |
| 469 | .i32 => return .f32_ceil, // when valtype is f16, we store it in i32. | 500 | .i32 => return .f32_ceil, // when valtype is f16, we store it in i32. |
| 470 | .f32 => return .f32_ceil, | 501 | .f32 => return .f32_ceil, |
| 471 | .f64 => return .f64_ceil, | 502 | .f64 => return .f64_ceil, |
| | 503 | .v128 => unreachable, // handled independently |
| 472 | }, | 504 | }, |
| 473 | .floor => switch (args.valtype1.?) { | 505 | .floor => switch (args.valtype1.?) { |
| 474 | .i64 => unreachable, | 506 | .i64 => unreachable, |
| 475 | .i32 => return .f32_floor, // when valtype is f16, we store it in i32. | 507 | .i32 => return .f32_floor, // when valtype is f16, we store it in i32. |
| 476 | .f32 => return .f32_floor, | 508 | .f32 => return .f32_floor, |
| 477 | .f64 => return .f64_floor, | 509 | .f64 => return .f64_floor, |
| | 510 | .v128 => unreachable, // handled independently |
| 478 | }, | 511 | }, |
| 479 | .trunc => switch (args.valtype1.?) { | 512 | .trunc => switch (args.valtype1.?) { |
| 480 | .i32 => if (args.valtype2) |valty| switch (valty) { | 513 | .i32 => if (args.valtype2) |valty| switch (valty) { |
| ... | @@ -482,40 +515,48 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -482,40 +515,48 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 482 | .i64 => unreachable, | 515 | .i64 => unreachable, |
| 483 | .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u, | 516 | .f32 => if (args.signedness.? == .signed) return .i32_trunc_f32_s else return .i32_trunc_f32_u, |
| 484 | .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u, | 517 | .f64 => if (args.signedness.? == .signed) return .i32_trunc_f64_s else return .i32_trunc_f64_u, |
| | 518 | .v128 => unreachable, // handled independently |
| 485 | } else return .f32_trunc, // when no valtype2, it's an f16 instead which is stored in an i32. | 519 | } else return .f32_trunc, // when no valtype2, it's an f16 instead which is stored in an i32. |
| 486 | .i64 => switch (args.valtype2.?) { | 520 | .i64 => switch (args.valtype2.?) { |
| 487 | .i32 => unreachable, | 521 | .i32 => unreachable, |
| 488 | .i64 => unreachable, | 522 | .i64 => unreachable, |
| 489 | .f32 => if (args.signedness.? == .signed) return .i64_trunc_f32_s else return .i64_trunc_f32_u, | 523 | .f32 => if (args.signedness.? == .signed) return .i64_trunc_f32_s else return .i64_trunc_f32_u, |
| 490 | .f64 => if (args.signedness.? == .signed) return .i64_trunc_f64_s else return .i64_trunc_f64_u, | 524 | .f64 => if (args.signedness.? == .signed) return .i64_trunc_f64_s else return .i64_trunc_f64_u, |
| | 525 | .v128 => unreachable, // handled independently |
| 491 | }, | 526 | }, |
| 492 | .f32 => return .f32_trunc, | 527 | .f32 => return .f32_trunc, |
| 493 | .f64 => return .f64_trunc, | 528 | .f64 => return .f64_trunc, |
| | 529 | .v128 => unreachable, // handled independently |
| 494 | }, | 530 | }, |
| 495 | .nearest => switch (args.valtype1.?) { | 531 | .nearest => switch (args.valtype1.?) { |
| 496 | .i32, .i64 => unreachable, | 532 | .i32, .i64 => unreachable, |
| 497 | .f32 => return .f32_nearest, | 533 | .f32 => return .f32_nearest, |
| 498 | .f64 => return .f64_nearest, | 534 | .f64 => return .f64_nearest, |
| | 535 | .v128 => unreachable, // handled independently |
| 499 | }, | 536 | }, |
| 500 | .sqrt => switch (args.valtype1.?) { | 537 | .sqrt => switch (args.valtype1.?) { |
| 501 | .i32, .i64 => unreachable, | 538 | .i32, .i64 => unreachable, |
| 502 | .f32 => return .f32_sqrt, | 539 | .f32 => return .f32_sqrt, |
| 503 | .f64 => return .f64_sqrt, | 540 | .f64 => return .f64_sqrt, |
| | 541 | .v128 => unreachable, // handled independently |
| 504 | }, | 542 | }, |
| 505 | .min => switch (args.valtype1.?) { | 543 | .min => switch (args.valtype1.?) { |
| 506 | .i32, .i64 => unreachable, | 544 | .i32, .i64 => unreachable, |
| 507 | .f32 => return .f32_min, | 545 | .f32 => return .f32_min, |
| 508 | .f64 => return .f64_min, | 546 | .f64 => return .f64_min, |
| | 547 | .v128 => unreachable, // handled independently |
| 509 | }, | 548 | }, |
| 510 | .max => switch (args.valtype1.?) { | 549 | .max => switch (args.valtype1.?) { |
| 511 | .i32, .i64 => unreachable, | 550 | .i32, .i64 => unreachable, |
| 512 | .f32 => return .f32_max, | 551 | .f32 => return .f32_max, |
| 513 | .f64 => return .f64_max, | 552 | .f64 => return .f64_max, |
| | 553 | .v128 => unreachable, // handled independently |
| 514 | }, | 554 | }, |
| 515 | .copysign => switch (args.valtype1.?) { | 555 | .copysign => switch (args.valtype1.?) { |
| 516 | .i32, .i64 => unreachable, | 556 | .i32, .i64 => unreachable, |
| 517 | .f32 => return .f32_copysign, | 557 | .f32 => return .f32_copysign, |
| 518 | .f64 => return .f64_copysign, | 558 | .f64 => return .f64_copysign, |
| | 559 | .v128 => unreachable, // handled independently |
| 519 | }, | 560 | }, |
| 520 | | 561 | |
| 521 | .wrap => switch (args.valtype1.?) { | 562 | .wrap => switch (args.valtype1.?) { |
| ... | @@ -523,8 +564,10 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -523,8 +564,10 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 523 | .i32 => unreachable, | 564 | .i32 => unreachable, |
| 524 | .i64 => return .i32_wrap_i64, | 565 | .i64 => return .i32_wrap_i64, |
| 525 | .f32, .f64 => unreachable, | 566 | .f32, .f64 => unreachable, |
| | 567 | .v128 => unreachable, // handled independently |
| 526 | }, | 568 | }, |
| 527 | .i64, .f32, .f64 => unreachable, | 569 | .i64, .f32, .f64 => unreachable, |
| | 570 | .v128 => unreachable, // handled independently |
| 528 | }, | 571 | }, |
| 529 | .convert => switch (args.valtype1.?) { | 572 | .convert => switch (args.valtype1.?) { |
| 530 | .i32, .i64 => unreachable, | 573 | .i32, .i64 => unreachable, |
| ... | @@ -532,12 +575,15 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -532,12 +575,15 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 532 | .i32 => if (args.signedness.? == .signed) return .f32_convert_i32_s else return .f32_convert_i32_u, | 575 | .i32 => if (args.signedness.? == .signed) return .f32_convert_i32_s else return .f32_convert_i32_u, |
| 533 | .i64 => if (args.signedness.? == .signed) return .f32_convert_i64_s else return .f32_convert_i64_u, | 576 | .i64 => if (args.signedness.? == .signed) return .f32_convert_i64_s else return .f32_convert_i64_u, |
| 534 | .f32, .f64 => unreachable, | 577 | .f32, .f64 => unreachable, |
| | 578 | .v128 => unreachable, // handled independently |
| 535 | }, | 579 | }, |
| 536 | .f64 => switch (args.valtype2.?) { | 580 | .f64 => switch (args.valtype2.?) { |
| 537 | .i32 => if (args.signedness.? == .signed) return .f64_convert_i32_s else return .f64_convert_i32_u, | 581 | .i32 => if (args.signedness.? == .signed) return .f64_convert_i32_s else return .f64_convert_i32_u, |
| 538 | .i64 => if (args.signedness.? == .signed) return .f64_convert_i64_s else return .f64_convert_i64_u, | 582 | .i64 => if (args.signedness.? == .signed) return .f64_convert_i64_s else return .f64_convert_i64_u, |
| 539 | .f32, .f64 => unreachable, | 583 | .f32, .f64 => unreachable, |
| | 584 | .v128 => unreachable, // handled independently |
| 540 | }, | 585 | }, |
| | 586 | .v128 => unreachable, // handled independently |
| 541 | }, | 587 | }, |
| 542 | .demote => if (args.valtype1.? == .f32 and args.valtype2.? == .f64) return .f32_demote_f64 else unreachable, | 588 | .demote => if (args.valtype1.? == .f32 and args.valtype2.? == .f64) return .f32_demote_f64 else unreachable, |
| 543 | .promote => if (args.valtype1.? == .f64 and args.valtype2.? == .f32) return .f64_promote_f32 else unreachable, | 589 | .promote => if (args.valtype1.? == .f64 and args.valtype2.? == .f32) return .f64_promote_f32 else unreachable, |
| ... | @@ -546,6 +592,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -546,6 +592,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 546 | .i64 => if (args.valtype2.? == .f64) return .i64_reinterpret_f64 else unreachable, | 592 | .i64 => if (args.valtype2.? == .f64) return .i64_reinterpret_f64 else unreachable, |
| 547 | .f32 => if (args.valtype2.? == .i32) return .f32_reinterpret_i32 else unreachable, | 593 | .f32 => if (args.valtype2.? == .i32) return .f32_reinterpret_i32 else unreachable, |
| 548 | .f64 => if (args.valtype2.? == .i64) return .f64_reinterpret_i64 else unreachable, | 594 | .f64 => if (args.valtype2.? == .i64) return .f64_reinterpret_i64 else unreachable, |
| | 595 | .v128 => unreachable, // handled independently |
| 549 | }, | 596 | }, |
| 550 | .extend => switch (args.valtype1.?) { | 597 | .extend => switch (args.valtype1.?) { |
| 551 | .i32 => switch (args.width.?) { | 598 | .i32 => switch (args.width.?) { |
| ... | @@ -560,6 +607,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { | ... | @@ -560,6 +607,7 @@ fn buildOpcode(args: OpcodeBuildArguments) wasm.Opcode { |
| 560 | else => unreachable, | 607 | else => unreachable, |
| 561 | }, | 608 | }, |
| 562 | .f32, .f64 => unreachable, | 609 | .f32, .f64 => unreachable, |
| | 610 | .v128 => unreachable, // handled independently |
| 563 | }, | 611 | }, |
| 564 | } | 612 | } |
| 565 | } | 613 | } |
| ... | @@ -629,6 +677,10 @@ err_msg: *Module.ErrorMsg, | ... | @@ -629,6 +677,10 @@ err_msg: *Module.ErrorMsg, |
| 629 | /// List of all locals' types generated throughout this declaration | 677 | /// List of all locals' types generated throughout this declaration |
| 630 | /// used to emit locals count at start of 'code' section. | 678 | /// used to emit locals count at start of 'code' section. |
| 631 | locals: std.ArrayListUnmanaged(u8), | 679 | locals: std.ArrayListUnmanaged(u8), |
| | 680 | /// List of simd128 immediates. Each value is stored as an array of bytes. |
| | 681 | /// This list will only be populated for 128bit-simd values when the target features |
| | 682 | /// are enabled also. |
| | 683 | simd_immediates: std.ArrayListUnmanaged([16]u8) = .{}, |
| 632 | /// The Target we're emitting (used to call intInfo) | 684 | /// The Target we're emitting (used to call intInfo) |
| 633 | target: std.Target, | 685 | target: std.Target, |
| 634 | /// Represents the wasm binary file that is being linked. | 686 | /// Represents the wasm binary file that is being linked. |
| ... | @@ -665,14 +717,17 @@ stack_alignment: u32 = 16, | ... | @@ -665,14 +717,17 @@ stack_alignment: u32 = 16, |
| 665 | /// It is illegal to store a non-i32 valtype in this list. | 717 | /// It is illegal to store a non-i32 valtype in this list. |
| 666 | free_locals_i32: std.ArrayListUnmanaged(u32) = .{}, | 718 | free_locals_i32: std.ArrayListUnmanaged(u32) = .{}, |
| 667 | /// A list of indexes which represents a local of valtype `i64`. | 719 | /// A list of indexes which represents a local of valtype `i64`. |
| 668 | /// It is illegal to store a non-i32 valtype in this list. | 720 | /// It is illegal to store a non-i64 valtype in this list. |
| 669 | free_locals_i64: std.ArrayListUnmanaged(u32) = .{}, | 721 | free_locals_i64: std.ArrayListUnmanaged(u32) = .{}, |
| 670 | /// A list of indexes which represents a local of valtype `f32`. | 722 | /// A list of indexes which represents a local of valtype `f32`. |
| 671 | /// It is illegal to store a non-i32 valtype in this list. | 723 | /// It is illegal to store a non-f32 valtype in this list. |
| 672 | free_locals_f32: std.ArrayListUnmanaged(u32) = .{}, | 724 | free_locals_f32: std.ArrayListUnmanaged(u32) = .{}, |
| 673 | /// A list of indexes which represents a local of valtype `f64`. | 725 | /// A list of indexes which represents a local of valtype `f64`. |
| 674 | /// It is illegal to store a non-i32 valtype in this list. | 726 | /// It is illegal to store a non-f64 valtype in this list. |
| 675 | free_locals_f64: std.ArrayListUnmanaged(u32) = .{}, | 727 | free_locals_f64: std.ArrayListUnmanaged(u32) = .{}, |
| | 728 | /// A list of indexes which represents a local of valtype `v127`. |
| | 729 | /// It is illegal to store a non-v128 valtype in this list. |
| | 730 | free_locals_v128: std.ArrayListUnmanaged(u32) = .{}, |
| 676 | | 731 | |
| 677 | /// When in debug mode, this tracks if no `finishAir` was missed. | 732 | /// When in debug mode, this tracks if no `finishAir` was missed. |
| 678 | /// Forgetting to call `finishAir` will cause the result to not be | 733 | /// Forgetting to call `finishAir` will cause the result to not be |
| ... | @@ -699,12 +754,14 @@ pub fn deinit(func: *CodeGen) void { | ... | @@ -699,12 +754,14 @@ pub fn deinit(func: *CodeGen) void { |
| 699 | func.branches.deinit(func.gpa); | 754 | func.branches.deinit(func.gpa); |
| 700 | func.blocks.deinit(func.gpa); | 755 | func.blocks.deinit(func.gpa); |
| 701 | func.locals.deinit(func.gpa); | 756 | func.locals.deinit(func.gpa); |
| | 757 | func.simd_immediates.deinit(func.gpa); |
| 702 | func.mir_instructions.deinit(func.gpa); | 758 | func.mir_instructions.deinit(func.gpa); |
| 703 | func.mir_extra.deinit(func.gpa); | 759 | func.mir_extra.deinit(func.gpa); |
| 704 | func.free_locals_i32.deinit(func.gpa); | 760 | func.free_locals_i32.deinit(func.gpa); |
| 705 | func.free_locals_i64.deinit(func.gpa); | 761 | func.free_locals_i64.deinit(func.gpa); |
| 706 | func.free_locals_f32.deinit(func.gpa); | 762 | func.free_locals_f32.deinit(func.gpa); |
| 707 | func.free_locals_f64.deinit(func.gpa); | 763 | func.free_locals_f64.deinit(func.gpa); |
| | 764 | func.free_locals_v128.deinit(func.gpa); |
| 708 | func.* = undefined; | 765 | func.* = undefined; |
| 709 | } | 766 | } |
| 710 | | 767 | |
| ... | @@ -867,6 +924,17 @@ fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void { | ... | @@ -867,6 +924,17 @@ fn addImm64(func: *CodeGen, imm: u64) error{OutOfMemory}!void { |
| 867 | try func.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } }); | 924 | try func.addInst(.{ .tag = .i64_const, .data = .{ .payload = extra_index } }); |
| 868 | } | 925 | } |
| 869 | | 926 | |
| | 927 | /// Accepts the index into the list of 128bit-immediates |
| | 928 | fn addImm128(func: *CodeGen, index: u32) error{OutOfMemory}!void { |
| | 929 | const simd_values = func.simd_immediates.items[index]; |
| | 930 | const extra_index = @intCast(u32, func.mir_extra.items.len); |
| | 931 | // tag + 128bit value |
| | 932 | try func.mir_extra.ensureUnusedCapacity(func.gpa, 5); |
| | 933 | func.mir_extra.appendAssumeCapacity(std.wasm.simdOpcode(.v128_const)); |
| | 934 | func.mir_extra.appendSliceAssumeCapacity(@alignCast(4, mem.bytesAsSlice(u32, &simd_values))); |
| | 935 | try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } }); |
| | 936 | } |
| | 937 | |
| 870 | fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void { | 938 | fn addFloat64(func: *CodeGen, float: f64) error{OutOfMemory}!void { |
| 871 | const extra_index = try func.addExtra(Mir.Float64.fromFloat64(float)); | 939 | const extra_index = try func.addExtra(Mir.Float64.fromFloat64(float)); |
| 872 | try func.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } }); | 940 | try func.addInst(.{ .tag = .f64_const, .data = .{ .payload = extra_index } }); |
| ... | @@ -924,6 +992,10 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype { | ... | @@ -924,6 +992,10 @@ fn typeToValtype(ty: Type, target: std.Target) wasm.Valtype { |
| 924 | }, | 992 | }, |
| 925 | else => wasm.Valtype.i32, | 993 | else => wasm.Valtype.i32, |
| 926 | }, | 994 | }, |
| | 995 | .Vector => switch (determineSimdStoreStrategy(ty, target)) { |
| | 996 | .direct => wasm.Valtype.v128, |
| | 997 | .unrolled => wasm.Valtype.i32, |
| | 998 | }, |
| 927 | else => wasm.Valtype.i32, // all represented as reference/immediate | 999 | else => wasm.Valtype.i32, // all represented as reference/immediate |
| 928 | }; | 1000 | }; |
| 929 | } | 1001 | } |
| ... | @@ -950,6 +1022,7 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void { | ... | @@ -950,6 +1022,7 @@ fn emitWValue(func: *CodeGen, value: WValue) InnerError!void { |
| 950 | .local => |idx| try func.addLabel(.local_get, idx.value), | 1022 | .local => |idx| try func.addLabel(.local_get, idx.value), |
| 951 | .imm32 => |val| try func.addImm32(@bitCast(i32, val)), | 1023 | .imm32 => |val| try func.addImm32(@bitCast(i32, val)), |
| 952 | .imm64 => |val| try func.addImm64(val), | 1024 | .imm64 => |val| try func.addImm64(val), |
| | 1025 | .imm128 => |val| try func.addImm128(val), |
| 953 | .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), | 1026 | .float32 => |val| try func.addInst(.{ .tag = .f32_const, .data = .{ .float32 = val } }), |
| 954 | .float64 => |val| try func.addFloat64(val), | 1027 | .float64 => |val| try func.addFloat64(val), |
| 955 | .memory => |ptr| { | 1028 | .memory => |ptr| { |
| ... | @@ -1016,6 +1089,10 @@ fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue { | ... | @@ -1016,6 +1089,10 @@ fn allocLocal(func: *CodeGen, ty: Type) InnerError!WValue { |
| 1016 | log.debug("reusing local ({d}) of type {}\n", .{ index, valtype }); | 1089 | log.debug("reusing local ({d}) of type {}\n", .{ index, valtype }); |
| 1017 | return WValue{ .local = .{ .value = index, .references = 1 } }; | 1090 | return WValue{ .local = .{ .value = index, .references = 1 } }; |
| 1018 | }, | 1091 | }, |
| | 1092 | .v128 => if (func.free_locals_v128.popOrNull()) |index| { |
| | 1093 | log.debug("reusing local ({d}) of type {}\n", .{ index, valtype }); |
| | 1094 | return WValue{ .local = .{ .value = index, .references = 1 } }; |
| | 1095 | }, |
| 1019 | } | 1096 | } |
| 1020 | log.debug("new local of type {}\n", .{valtype}); | 1097 | log.debug("new local of type {}\n", .{valtype}); |
| 1021 | // no local was free to be re-used, so allocate a new local instead | 1098 | // no local was free to be re-used, so allocate a new local instead |
| ... | @@ -1098,7 +1175,6 @@ pub fn generate( | ... | @@ -1098,7 +1175,6 @@ pub fn generate( |
| 1098 | .gpa = bin_file.allocator, | 1175 | .gpa = bin_file.allocator, |
| 1099 | .air = air, | 1176 | .air = air, |
| 1100 | .liveness = liveness, | 1177 | .liveness = liveness, |
| 1101 | // .values = .{}, | | |
| 1102 | .code = code, | 1178 | .code = code, |
| 1103 | .decl_index = func.owner_decl, | 1179 | .decl_index = func.owner_decl, |
| 1104 | .decl = bin_file.options.module.?.declPtr(func.owner_decl), | 1180 | .decl = bin_file.options.module.?.declPtr(func.owner_decl), |
| ... | @@ -1481,9 +1557,9 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { | ... | @@ -1481,9 +1557,9 @@ fn memcpy(func: *CodeGen, dst: WValue, src: WValue, len: WValue) !void { |
| 1481 | .imm64 => |val| val, | 1557 | .imm64 => |val| val, |
| 1482 | else => unreachable, | 1558 | else => unreachable, |
| 1483 | }; | 1559 | }; |
| 1484 | // if the size (length) is more than 1024 bytes, we use a runtime loop instead to prevent | 1560 | // if the size (length) is more than 32 bytes, we use a runtime loop instead to prevent |
| 1485 | // binary size bloat. | 1561 | // binary size bloat. |
| 1486 | if (length > 1024) break :blk; | 1562 | if (length > 32) break :blk; |
| 1487 | var offset: u32 = 0; | 1563 | var offset: u32 = 0; |
| 1488 | const lhs_base = dst.offset(); | 1564 | const lhs_base = dst.offset(); |
| 1489 | const rhs_base = src.offset(); | 1565 | const rhs_base = src.offset(); |
| ... | @@ -1612,7 +1688,6 @@ fn isByRef(ty: Type, target: std.Target) bool { | ... | @@ -1612,7 +1688,6 @@ fn isByRef(ty: Type, target: std.Target) bool { |
| 1612 | => return false, | 1688 | => return false, |
| 1613 | | 1689 | |
| 1614 | .Array, | 1690 | .Array, |
| 1615 | .Vector, | | |
| 1616 | .Frame, | 1691 | .Frame, |
| 1617 | .Union, | 1692 | .Union, |
| 1618 | => return ty.hasRuntimeBitsIgnoreComptime(), | 1693 | => return ty.hasRuntimeBitsIgnoreComptime(), |
| ... | @@ -1625,6 +1700,7 @@ fn isByRef(ty: Type, target: std.Target) bool { | ... | @@ -1625,6 +1700,7 @@ fn isByRef(ty: Type, target: std.Target) bool { |
| 1625 | } | 1700 | } |
| 1626 | return ty.hasRuntimeBitsIgnoreComptime(); | 1701 | return ty.hasRuntimeBitsIgnoreComptime(); |
| 1627 | }, | 1702 | }, |
| | 1703 | .Vector => return determineSimdStoreStrategy(ty, target) == .unrolled, |
| 1628 | .Int => return ty.intInfo(target).bits > 64, | 1704 | .Int => return ty.intInfo(target).bits > 64, |
| 1629 | .Float => return ty.floatBits(target) > 64, | 1705 | .Float => return ty.floatBits(target) > 64, |
| 1630 | .ErrorUnion => { | 1706 | .ErrorUnion => { |
| ... | @@ -1647,6 +1723,26 @@ fn isByRef(ty: Type, target: std.Target) bool { | ... | @@ -1647,6 +1723,26 @@ fn isByRef(ty: Type, target: std.Target) bool { |
| 1647 | } | 1723 | } |
| 1648 | } | 1724 | } |
| 1649 | | 1725 | |
| | 1726 | const SimdStoreStrategy = enum { |
| | 1727 | direct, |
| | 1728 | unrolled, |
| | 1729 | }; |
| | 1730 | |
| | 1731 | /// For a given vector type, returns the `SimdStoreStrategy`. |
| | 1732 | /// This means when a given type is 128 bits and either the simd128 or relaxed-simd |
| | 1733 | /// features are enabled, the function will return `.direct`. This would allow to store |
| | 1734 | /// it using a instruction, rather than an unrolled version. |
| | 1735 | fn determineSimdStoreStrategy(ty: Type, target: std.Target) SimdStoreStrategy { |
| | 1736 | std.debug.assert(ty.zigTypeTag() == .Vector); |
| | 1737 | if (ty.bitSize(target) != 128) return .unrolled; |
| | 1738 | const hasFeature = std.Target.wasm.featureSetHas; |
| | 1739 | const features = target.cpu.features; |
| | 1740 | if (hasFeature(features, .relaxed_simd) or hasFeature(features, .simd128)) { |
| | 1741 | return .direct; |
| | 1742 | } |
| | 1743 | return .unrolled; |
| | 1744 | } |
| | 1745 | |
| 1650 | /// Creates a new local for a pointer that points to memory with given offset. | 1746 | /// Creates a new local for a pointer that points to memory with given offset. |
| 1651 | /// This can be used to get a pointer to a struct field, error payload, etc. | 1747 | /// This can be used to get a pointer to a struct field, error payload, etc. |
| 1652 | /// By providing `modify` as action, it will modify the given `ptr_value` instead of making a new | 1748 | /// By providing `modify` as action, it will modify the given `ptr_value` instead of making a new |
| ... | @@ -2187,10 +2283,29 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE | ... | @@ -2187,10 +2283,29 @@ fn store(func: *CodeGen, lhs: WValue, rhs: WValue, ty: Type, offset: u32) InnerE |
| 2187 | const len = @intCast(u32, ty.abiSize(func.target)); | 2283 | const len = @intCast(u32, ty.abiSize(func.target)); |
| 2188 | return func.memcpy(lhs, rhs, .{ .imm32 = len }); | 2284 | return func.memcpy(lhs, rhs, .{ .imm32 = len }); |
| 2189 | }, | 2285 | }, |
| 2190 | .Struct, .Array, .Union, .Vector => if (isByRef(ty, func.target)) { | 2286 | .Struct, .Array, .Union => if (isByRef(ty, func.target)) { |
| 2191 | const len = @intCast(u32, ty.abiSize(func.target)); | 2287 | const len = @intCast(u32, ty.abiSize(func.target)); |
| 2192 | return func.memcpy(lhs, rhs, .{ .imm32 = len }); | 2288 | return func.memcpy(lhs, rhs, .{ .imm32 = len }); |
| 2193 | }, | 2289 | }, |
| | 2290 | .Vector => switch (determineSimdStoreStrategy(ty, func.target)) { |
| | 2291 | .unrolled => { |
| | 2292 | const len = @intCast(u32, ty.abiSize(func.target)); |
| | 2293 | return func.memcpy(lhs, rhs, .{ .imm32 = len }); |
| | 2294 | }, |
| | 2295 | .direct => { |
| | 2296 | try func.emitWValue(lhs); |
| | 2297 | try func.lowerToStack(rhs); |
| | 2298 | // TODO: Add helper functions for simd opcodes |
| | 2299 | const extra_index = @intCast(u32, func.mir_extra.items.len); |
| | 2300 | // stores as := opcode, offset, alignment (opcode::memarg) |
| | 2301 | try func.mir_extra.appendSlice(func.gpa, &[_]u32{ |
| | 2302 | std.wasm.simdOpcode(.v128_store), |
| | 2303 | offset + lhs.offset(), |
| | 2304 | ty.abiAlignment(func.target), |
| | 2305 | }); |
| | 2306 | return func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } }); |
| | 2307 | }, |
| | 2308 | }, |
| 2194 | .Pointer => { | 2309 | .Pointer => { |
| 2195 | if (ty.isSlice()) { | 2310 | if (ty.isSlice()) { |
| 2196 | // store pointer first | 2311 | // store pointer first |
| ... | @@ -2289,6 +2404,19 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu | ... | @@ -2289,6 +2404,19 @@ fn load(func: *CodeGen, operand: WValue, ty: Type, offset: u32) InnerError!WValu |
| 2289 | // load local's value from memory by its stack position | 2404 | // load local's value from memory by its stack position |
| 2290 | try func.emitWValue(operand); | 2405 | try func.emitWValue(operand); |
| 2291 | | 2406 | |
| | 2407 | if (ty.zigTypeTag() == .Vector) { |
| | 2408 | // TODO: Add helper functions for simd opcodes |
| | 2409 | const extra_index = @intCast(u32, func.mir_extra.items.len); |
| | 2410 | // stores as := opcode, offset, alignment (opcode::memarg) |
| | 2411 | try func.mir_extra.appendSlice(func.gpa, &[_]u32{ |
| | 2412 | std.wasm.simdOpcode(.v128_load), |
| | 2413 | offset + operand.offset(), |
| | 2414 | ty.abiAlignment(func.target), |
| | 2415 | }); |
| | 2416 | try func.addInst(.{ .tag = .simd, .data = .{ .payload = extra_index } }); |
| | 2417 | return WValue{ .stack = {} }; |
| | 2418 | } |
| | 2419 | |
| 2292 | const abi_size = @intCast(u8, ty.abiSize(func.target)); | 2420 | const abi_size = @intCast(u8, ty.abiSize(func.target)); |
| 2293 | const opcode = buildOpcode(.{ | 2421 | const opcode = buildOpcode(.{ |
| 2294 | .valtype1 = typeToValtype(ty, func.target), | 2422 | .valtype1 = typeToValtype(ty, func.target), |
| ... | @@ -2766,10 +2894,24 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { | ... | @@ -2766,10 +2894,24 @@ fn lowerConstant(func: *CodeGen, arg_val: Value, ty: Type) InnerError!WValue { |
| 2766 | const int_val = Value.initPayload(&payload.base); | 2894 | const int_val = Value.initPayload(&payload.base); |
| 2767 | return func.lowerConstant(int_val, struct_obj.backing_int_ty); | 2895 | return func.lowerConstant(int_val, struct_obj.backing_int_ty); |
| 2768 | }, | 2896 | }, |
| | 2897 | .Vector => { |
| | 2898 | assert(determineSimdStoreStrategy(ty, target) == .direct); |
| | 2899 | var buf: [16]u8 = undefined; |
| | 2900 | val.writeToMemory(ty, func.bin_file.base.options.module.?, &buf); |
| | 2901 | return func.storeSimdImmd(buf); |
| | 2902 | }, |
| 2769 | else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}), | 2903 | else => |zig_type| return func.fail("Wasm TODO: LowerConstant for zigTypeTag {}", .{zig_type}), |
| 2770 | } | 2904 | } |
| 2771 | } | 2905 | } |
| 2772 | | 2906 | |
| | 2907 | /// Stores the value as a 128bit-immediate value by storing it inside |
| | 2908 | /// the list and returning the index into this list as `WValue`. |
| | 2909 | fn storeSimdImmd(func: *CodeGen, value: [16]u8) !WValue { |
| | 2910 | const index = @intCast(u32, func.simd_immediates.items.len); |
| | 2911 | try func.simd_immediates.append(func.gpa, value); |
| | 2912 | return WValue{ .imm128 = index }; |
| | 2913 | } |
| | 2914 | |
| 2773 | fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue { | 2915 | fn emitUndefined(func: *CodeGen, ty: Type) InnerError!WValue { |
| 2774 | switch (ty.zigTypeTag()) { | 2916 | switch (ty.zigTypeTag()) { |
| 2775 | .Bool, .ErrorSet => return WValue{ .imm32 = 0xaaaaaaaa }, | 2917 | .Bool, .ErrorSet => return WValue{ .imm32 = 0xaaaaaaaa }, |