1; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py 2; RUN: llc < %s -disable-wasm-fallthrough-return-opt -wasm-keep-registers | FileCheck %s 3 4; Test that basic 32-bit floating-point comparison operations assemble as 5; expected. 6 7target triple = "wasm32-unknown-unknown" 8 9define i32 @ord_f32(float %x, float %y) { 10; CHECK-LABEL: ord_f32: 11; CHECK: .functype ord_f32 (f32, f32) -> (i32) 12; CHECK-NEXT: # %bb.0: 13; CHECK-NEXT: local.get $push4=, 0 14; CHECK-NEXT: local.get $push3=, 0 15; CHECK-NEXT: f32.eq $push1=, $pop4, $pop3 16; CHECK-NEXT: local.get $push6=, 1 17; CHECK-NEXT: local.get $push5=, 1 18; CHECK-NEXT: f32.eq $push0=, $pop6, $pop5 19; CHECK-NEXT: i32.and $push2=, $pop1, $pop0 20; CHECK-NEXT: return $pop2 21 %a = fcmp ord float %x, %y 22 %b = zext i1 %a to i32 23 ret i32 %b 24} 25 26define i32 @uno_f32(float %x, float %y) { 27; CHECK-LABEL: uno_f32: 28; CHECK: .functype uno_f32 (f32, f32) -> (i32) 29; CHECK-NEXT: # %bb.0: 30; CHECK-NEXT: local.get $push4=, 0 31; CHECK-NEXT: local.get $push3=, 0 32; CHECK-NEXT: f32.ne $push1=, $pop4, $pop3 33; CHECK-NEXT: local.get $push6=, 1 34; CHECK-NEXT: local.get $push5=, 1 35; CHECK-NEXT: f32.ne $push0=, $pop6, $pop5 36; CHECK-NEXT: i32.or $push2=, $pop1, $pop0 37; CHECK-NEXT: return $pop2 38 %a = fcmp uno float %x, %y 39 %b = zext i1 %a to i32 40 ret i32 %b 41} 42 43define i32 @oeq_f32(float %x, float %y) { 44; CHECK-LABEL: oeq_f32: 45; CHECK: .functype oeq_f32 (f32, f32) -> (i32) 46; CHECK-NEXT: # %bb.0: 47; CHECK-NEXT: local.get $push2=, 0 48; CHECK-NEXT: local.get $push1=, 1 49; CHECK-NEXT: f32.eq $push0=, $pop2, $pop1 50; CHECK-NEXT: return $pop0 51 %a = fcmp oeq float %x, %y 52 %b = zext i1 %a to i32 53 ret i32 %b 54} 55 56define i32 @une_f32(float %x, float %y) { 57; CHECK-LABEL: une_f32: 58; CHECK: .functype une_f32 (f32, f32) -> (i32) 59; CHECK-NEXT: # %bb.0: 60; CHECK-NEXT: local.get $push2=, 0 61; CHECK-NEXT: local.get $push1=, 1 62; CHECK-NEXT: f32.ne $push0=, $pop2, $pop1 63; CHECK-NEXT: return $pop0 64 %a = fcmp une float %x, %y 65 %b = zext i1 %a to i32 66 ret i32 %b 67} 68 69define i32 @olt_f32(float %x, float %y) { 70; CHECK-LABEL: olt_f32: 71; CHECK: .functype olt_f32 (f32, f32) -> (i32) 72; CHECK-NEXT: # %bb.0: 73; CHECK-NEXT: local.get $push2=, 0 74; CHECK-NEXT: local.get $push1=, 1 75; CHECK-NEXT: f32.lt $push0=, $pop2, $pop1 76; CHECK-NEXT: return $pop0 77 %a = fcmp olt float %x, %y 78 %b = zext i1 %a to i32 79 ret i32 %b 80} 81 82define i32 @ole_f32(float %x, float %y) { 83; CHECK-LABEL: ole_f32: 84; CHECK: .functype ole_f32 (f32, f32) -> (i32) 85; CHECK-NEXT: # %bb.0: 86; CHECK-NEXT: local.get $push2=, 0 87; CHECK-NEXT: local.get $push1=, 1 88; CHECK-NEXT: f32.le $push0=, $pop2, $pop1 89; CHECK-NEXT: return $pop0 90 %a = fcmp ole float %x, %y 91 %b = zext i1 %a to i32 92 ret i32 %b 93} 94 95define i32 @ogt_f32(float %x, float %y) { 96; CHECK-LABEL: ogt_f32: 97; CHECK: .functype ogt_f32 (f32, f32) -> (i32) 98; CHECK-NEXT: # %bb.0: 99; CHECK-NEXT: local.get $push2=, 0 100; CHECK-NEXT: local.get $push1=, 1 101; CHECK-NEXT: f32.gt $push0=, $pop2, $pop1 102; CHECK-NEXT: return $pop0 103 %a = fcmp ogt float %x, %y 104 %b = zext i1 %a to i32 105 ret i32 %b 106} 107 108define i32 @oge_f32(float %x, float %y) { 109; CHECK-LABEL: oge_f32: 110; CHECK: .functype oge_f32 (f32, f32) -> (i32) 111; CHECK-NEXT: # %bb.0: 112; CHECK-NEXT: local.get $push2=, 0 113; CHECK-NEXT: local.get $push1=, 1 114; CHECK-NEXT: f32.ge $push0=, $pop2, $pop1 115; CHECK-NEXT: return $pop0 116 %a = fcmp oge float %x, %y 117 %b = zext i1 %a to i32 118 ret i32 %b 119} 120 121; Expanded comparisons, which also check for NaN. 122; These simply rely on SDAG's Expand cond code action. 123 124define i32 @ueq_f32(float %x, float %y) { 125; CHECK-LABEL: ueq_f32: 126; CHECK: .functype ueq_f32 (f32, f32) -> (i32) 127; CHECK-NEXT: # %bb.0: 128; CHECK-NEXT: local.get $push6=, 0 129; CHECK-NEXT: local.get $push5=, 1 130; CHECK-NEXT: f32.gt $push1=, $pop6, $pop5 131; CHECK-NEXT: local.get $push8=, 0 132; CHECK-NEXT: local.get $push7=, 1 133; CHECK-NEXT: f32.lt $push0=, $pop8, $pop7 134; CHECK-NEXT: i32.or $push2=, $pop1, $pop0 135; CHECK-NEXT: i32.const $push3=, 1 136; CHECK-NEXT: i32.xor $push4=, $pop2, $pop3 137; CHECK-NEXT: return $pop4 138 %a = fcmp ueq float %x, %y 139 %b = zext i1 %a to i32 140 ret i32 %b 141} 142 143define i32 @one_f32(float %x, float %y) { 144; CHECK-LABEL: one_f32: 145; CHECK: .functype one_f32 (f32, f32) -> (i32) 146; CHECK-NEXT: # %bb.0: 147; CHECK-NEXT: local.get $push4=, 0 148; CHECK-NEXT: local.get $push3=, 1 149; CHECK-NEXT: f32.gt $push1=, $pop4, $pop3 150; CHECK-NEXT: local.get $push6=, 0 151; CHECK-NEXT: local.get $push5=, 1 152; CHECK-NEXT: f32.lt $push0=, $pop6, $pop5 153; CHECK-NEXT: i32.or $push2=, $pop1, $pop0 154; CHECK-NEXT: return $pop2 155 %a = fcmp one float %x, %y 156 %b = zext i1 %a to i32 157 ret i32 %b 158} 159 160define i32 @ult_f32(float %x, float %y) { 161; CHECK-LABEL: ult_f32: 162; CHECK: .functype ult_f32 (f32, f32) -> (i32) 163; CHECK-NEXT: # %bb.0: 164; CHECK-NEXT: local.get $push4=, 0 165; CHECK-NEXT: local.get $push3=, 1 166; CHECK-NEXT: f32.ge $push0=, $pop4, $pop3 167; CHECK-NEXT: i32.const $push1=, 1 168; CHECK-NEXT: i32.xor $push2=, $pop0, $pop1 169; CHECK-NEXT: return $pop2 170 %a = fcmp ult float %x, %y 171 %b = zext i1 %a to i32 172 ret i32 %b 173} 174 175define i32 @ule_f32(float %x, float %y) { 176; CHECK-LABEL: ule_f32: 177; CHECK: .functype ule_f32 (f32, f32) -> (i32) 178; CHECK-NEXT: # %bb.0: 179; CHECK-NEXT: local.get $push4=, 0 180; CHECK-NEXT: local.get $push3=, 1 181; CHECK-NEXT: f32.gt $push0=, $pop4, $pop3 182; CHECK-NEXT: i32.const $push1=, 1 183; CHECK-NEXT: i32.xor $push2=, $pop0, $pop1 184; CHECK-NEXT: return $pop2 185 %a = fcmp ule float %x, %y 186 %b = zext i1 %a to i32 187 ret i32 %b 188} 189 190define i32 @ugt_f32(float %x, float %y) { 191; CHECK-LABEL: ugt_f32: 192; CHECK: .functype ugt_f32 (f32, f32) -> (i32) 193; CHECK-NEXT: # %bb.0: 194; CHECK-NEXT: local.get $push4=, 0 195; CHECK-NEXT: local.get $push3=, 1 196; CHECK-NEXT: f32.le $push0=, $pop4, $pop3 197; CHECK-NEXT: i32.const $push1=, 1 198; CHECK-NEXT: i32.xor $push2=, $pop0, $pop1 199; CHECK-NEXT: return $pop2 200 %a = fcmp ugt float %x, %y 201 %b = zext i1 %a to i32 202 ret i32 %b 203} 204 205define i32 @uge_f32(float %x, float %y) { 206; CHECK-LABEL: uge_f32: 207; CHECK: .functype uge_f32 (f32, f32) -> (i32) 208; CHECK-NEXT: # %bb.0: 209; CHECK-NEXT: local.get $push4=, 0 210; CHECK-NEXT: local.get $push3=, 1 211; CHECK-NEXT: f32.lt $push0=, $pop4, $pop3 212; CHECK-NEXT: i32.const $push1=, 1 213; CHECK-NEXT: i32.xor $push2=, $pop0, $pop1 214; CHECK-NEXT: return $pop2 215 %a = fcmp uge float %x, %y 216 %b = zext i1 %a to i32 217 ret i32 %b 218} 219 220define void @olt_f32_branch(float %a, float %b) { 221; CHECK-LABEL: olt_f32_branch: 222; CHECK: .functype olt_f32_branch (f32, f32) -> () 223; CHECK-NEXT: # %bb.0: # %entry 224; CHECK-NEXT: block 225; CHECK-NEXT: local.get $push2=, 0 226; CHECK-NEXT: local.get $push1=, 1 227; CHECK-NEXT: f32.lt $push0=, $pop2, $pop1 228; CHECK-NEXT: i32.eqz $push3=, $pop0 229; CHECK-NEXT: br_if 0, $pop3 # 0: down to label0 230; CHECK-NEXT: # %bb.1: # %if.then 231; CHECK-NEXT: call call1 232; CHECK-NEXT: .LBB14_2: # %if.end 233; CHECK-NEXT: end_block # label0: 234; CHECK-NEXT: return 235entry: 236 %cmp = fcmp olt float %a, %b 237 br i1 %cmp, label %if.then, label %if.end 238 239if.then: 240 tail call void @call1() 241 br label %if.end 242 243if.end: 244 ret void 245} 246 247define void @ole_f32_branch(float %a, float %b) { 248; CHECK-LABEL: ole_f32_branch: 249; CHECK: .functype ole_f32_branch (f32, f32) -> () 250; CHECK-NEXT: # %bb.0: # %entry 251; CHECK-NEXT: block 252; CHECK-NEXT: local.get $push2=, 0 253; CHECK-NEXT: local.get $push1=, 1 254; CHECK-NEXT: f32.le $push0=, $pop2, $pop1 255; CHECK-NEXT: i32.eqz $push3=, $pop0 256; CHECK-NEXT: br_if 0, $pop3 # 0: down to label1 257; CHECK-NEXT: # %bb.1: # %if.then 258; CHECK-NEXT: call call1 259; CHECK-NEXT: .LBB15_2: # %if.end 260; CHECK-NEXT: end_block # label1: 261; CHECK-NEXT: return 262entry: 263 %cmp = fcmp ole float %a, %b 264 br i1 %cmp, label %if.then, label %if.end 265 266if.then: 267 tail call void @call1() 268 br label %if.end 269 270if.end: 271 ret void 272} 273 274define void @ugt_f32_branch(float %a, float %b) { 275; CHECK-LABEL: ugt_f32_branch: 276; CHECK: .functype ugt_f32_branch (f32, f32) -> () 277; CHECK-NEXT: # %bb.0: # %entry 278; CHECK-NEXT: block 279; CHECK-NEXT: local.get $push2=, 0 280; CHECK-NEXT: local.get $push1=, 1 281; CHECK-NEXT: f32.le $push0=, $pop2, $pop1 282; CHECK-NEXT: i32.eqz $push3=, $pop0 283; CHECK-NEXT: br_if 0, $pop3 # 0: down to label2 284; CHECK-NEXT: # %bb.1: # %if.then 285; CHECK-NEXT: call call1 286; CHECK-NEXT: .LBB16_2: # %if.end 287; CHECK-NEXT: end_block # label2: 288; CHECK-NEXT: return 289entry: 290 %cmp = fcmp ugt float %a, %b 291 br i1 %cmp, label %if.end, label %if.then 292 293if.then: 294 tail call void @call1() 295 br label %if.end 296 297if.end: 298 ret void 299} 300 301define void @ogt_f32_branch(float %a, float %b) { 302; CHECK-LABEL: ogt_f32_branch: 303; CHECK: .functype ogt_f32_branch (f32, f32) -> () 304; CHECK-NEXT: # %bb.0: # %entry 305; CHECK-NEXT: block 306; CHECK-NEXT: local.get $push2=, 0 307; CHECK-NEXT: local.get $push1=, 1 308; CHECK-NEXT: f32.gt $push0=, $pop2, $pop1 309; CHECK-NEXT: i32.eqz $push3=, $pop0 310; CHECK-NEXT: br_if 0, $pop3 # 0: down to label3 311; CHECK-NEXT: # %bb.1: # %if.then 312; CHECK-NEXT: call call1 313; CHECK-NEXT: .LBB17_2: # %if.end 314; CHECK-NEXT: end_block # label3: 315; CHECK-NEXT: return 316entry: 317 %cmp = fcmp ogt float %a, %b 318 br i1 %cmp, label %if.then, label %if.end 319 320if.then: 321 tail call void @call1() 322 br label %if.end 323 324if.end: 325 ret void 326} 327 328define void @ult_f32_branch(float %a, float %b) { 329; CHECK-LABEL: ult_f32_branch: 330; CHECK: .functype ult_f32_branch (f32, f32) -> () 331; CHECK-NEXT: # %bb.0: # %entry 332; CHECK-NEXT: block 333; CHECK-NEXT: local.get $push2=, 0 334; CHECK-NEXT: local.get $push1=, 1 335; CHECK-NEXT: f32.ge $push0=, $pop2, $pop1 336; CHECK-NEXT: i32.eqz $push3=, $pop0 337; CHECK-NEXT: br_if 0, $pop3 # 0: down to label4 338; CHECK-NEXT: # %bb.1: # %if.then 339; CHECK-NEXT: call call1 340; CHECK-NEXT: .LBB18_2: # %if.end 341; CHECK-NEXT: end_block # label4: 342; CHECK-NEXT: return 343entry: 344 %cmp = fcmp ult float %a, %b 345 br i1 %cmp, label %if.end, label %if.then 346 347if.then: 348 tail call void @call1() 349 br label %if.end 350 351if.end: 352 ret void 353} 354 355define void @ule_f32_branch(float %a, float %b) { 356; CHECK-LABEL: ule_f32_branch: 357; CHECK: .functype ule_f32_branch (f32, f32) -> () 358; CHECK-NEXT: # %bb.0: # %entry 359; CHECK-NEXT: block 360; CHECK-NEXT: local.get $push2=, 0 361; CHECK-NEXT: local.get $push1=, 1 362; CHECK-NEXT: f32.ge $push0=, $pop2, $pop1 363; CHECK-NEXT: i32.eqz $push3=, $pop0 364; CHECK-NEXT: br_if 0, $pop3 # 0: down to label5 365; CHECK-NEXT: # %bb.1: # %if.then 366; CHECK-NEXT: call call1 367; CHECK-NEXT: .LBB19_2: # %if.end 368; CHECK-NEXT: end_block # label5: 369; CHECK-NEXT: return 370entry: 371 %cmp = fcmp ult float %a, %b 372 br i1 %cmp, label %if.end, label %if.then 373 374if.then: 375 tail call void @call1() 376 br label %if.end 377 378if.end: 379 ret void 380} 381 382define void @xor_zext_switch(float %a, float %b) { 383; CHECK-LABEL: xor_zext_switch: 384; CHECK: .functype xor_zext_switch (f32, f32) -> () 385; CHECK-NEXT: # %bb.0: # %entry 386; CHECK-NEXT: block 387; CHECK-NEXT: i32.const $push1=, 0 388; CHECK-NEXT: br_if 0, $pop1 # 0: down to label6 389; CHECK-NEXT: # %bb.1: # %entry 390; CHECK-NEXT: block 391; CHECK-NEXT: block 392; CHECK-NEXT: local.get $push3=, 0 393; CHECK-NEXT: local.get $push2=, 1 394; CHECK-NEXT: f32.ge $push0=, $pop3, $pop2 395; CHECK-NEXT: br_table $pop0, 0, 1, 0 # 0: down to label8 396; CHECK-NEXT: # 1: down to label7 397; CHECK-NEXT: .LBB20_2: # %sw.bb.1 398; CHECK-NEXT: end_block # label8: 399; CHECK-NEXT: call foo1 400; CHECK-NEXT: return 401; CHECK-NEXT: .LBB20_3: # %sw.bb.2 402; CHECK-NEXT: end_block # label7: 403; CHECK-NEXT: call foo2 404; CHECK-NEXT: .LBB20_4: # %exit 405; CHECK-NEXT: end_block # label6: 406; CHECK-NEXT: return 407entry: 408 %cmp = fcmp ult float %a, %b 409 %zext = zext i1 %cmp to i32 410 %xor = xor i32 %zext, 1 411 switch i32 %xor, label %exit [ 412 i32 0, label %sw.bb.1 413 i32 1, label %sw.bb.2 414 ] 415 416sw.bb.1: 417 tail call void @foo1() 418 br label %exit 419 420sw.bb.2: 421 tail call void @foo2() 422 br label %exit 423 424exit: 425 ret void 426} 427 428define void @xor_add_switch(float %a, float %b) { 429; CHECK-LABEL: xor_add_switch: 430; CHECK: .functype xor_add_switch (f32, f32) -> () 431; CHECK-NEXT: # %bb.0: # %entry 432; CHECK-NEXT: block 433; CHECK-NEXT: block 434; CHECK-NEXT: block 435; CHECK-NEXT: block 436; CHECK-NEXT: local.get $push8=, 0 437; CHECK-NEXT: local.get $push7=, 1 438; CHECK-NEXT: f32.ge $push1=, $pop8, $pop7 439; CHECK-NEXT: i32.const $push2=, 1 440; CHECK-NEXT: i32.xor $push3=, $pop1, $pop2 441; CHECK-NEXT: i32.const $push6=, 1 442; CHECK-NEXT: i32.add $push4=, $pop3, $pop6 443; CHECK-NEXT: i32.const $push5=, 1 444; CHECK-NEXT: i32.xor $push0=, $pop4, $pop5 445; CHECK-NEXT: br_table $pop0, 0, 1, 2, 3 # 0: down to label12 446; CHECK-NEXT: # 1: down to label11 447; CHECK-NEXT: # 2: down to label10 448; CHECK-NEXT: # 3: down to label9 449; CHECK-NEXT: .LBB21_1: # %sw.bb.1 450; CHECK-NEXT: end_block # label12: 451; CHECK-NEXT: call foo1 452; CHECK-NEXT: return 453; CHECK-NEXT: .LBB21_2: # %sw.bb.2 454; CHECK-NEXT: end_block # label11: 455; CHECK-NEXT: call foo2 456; CHECK-NEXT: return 457; CHECK-NEXT: .LBB21_3: # %sw.bb.3 458; CHECK-NEXT: end_block # label10: 459; CHECK-NEXT: call foo3 460; CHECK-NEXT: .LBB21_4: # %exit 461; CHECK-NEXT: end_block # label9: 462; CHECK-NEXT: return 463entry: 464 %cmp = fcmp ult float %a, %b 465 %zext = zext i1 %cmp to i32 466 %add = add nsw nuw i32 %zext, 1 467 %xor = xor i32 %add, 1 468 switch i32 %xor, label %exit [ 469 i32 0, label %sw.bb.1 470 i32 1, label %sw.bb.2 471 i32 2, label %sw.bb.3 472 ] 473 474sw.bb.1: 475 tail call void @foo1() 476 br label %exit 477 478sw.bb.2: 479 tail call void @foo2() 480 br label %exit 481 482sw.bb.3: 483 tail call void @foo3() 484 br label %exit 485 486exit: 487 ret void 488} 489 490declare void @foo1() 491declare void @foo2() 492declare void @foo3() 493declare void @call1() 494