Index: llvm/trunk/test/CodeGen/Mips/atomicCmpSwapPW.ll =================================================================== --- llvm/trunk/test/CodeGen/Mips/atomicCmpSwapPW.ll (revision 336327) +++ llvm/trunk/test/CodeGen/Mips/atomicCmpSwapPW.ll (revision 336328) @@ -1,17 +1,113 @@ -; RUN: llc -O0 -march=mipsel -mcpu=mips32r2 -target-abi=o32 < %s -filetype=asm -o - \ -; RUN: | FileCheck -check-prefixes=PTR32,ALL %s -; RUN: llc -O0 -march=mips64el -mcpu=mips64r2 -target-abi=n32 < %s -filetype=asm -o - \ -; RUN: | FileCheck -check-prefixes=PTR32,ALL %s -; RUN: llc -O0 -march=mips64el -mcpu=mips64r2 -target-abi=n64 < %s -filetype=asm -o - \ -; RUN: | FileCheck -check-prefixes=PTR64,ALL %s +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -O0 -mtriple=mipsel-unknown-linux-gnu -mcpu=mips32r2 -target-abi=o32 < %s -filetype=asm -o - \ +; RUN: | FileCheck -check-prefixes=O32 %s +; RUN: llc -O0 -mtriple=mips64el-unknown-linux-gnu -mcpu=mips64r2 -target-abi=n32 < %s -filetype=asm -o - \ +; RUN: | FileCheck -check-prefixes=N32,ALL %s +; RUN: llc -O0 -mtriple=mips64el-unknown-linux-gnu -mcpu=mips64r2 -target-abi=n64 < %s -filetype=asm -o - \ +; RUN: | FileCheck -check-prefixes=N64 %s -; PTR32: lw $[[R0:[0-9]+]] -; PTR64: ld $[[R0:[0-9]+]] +@sym = external global i32 * -; ALL: ll ${{[0-9]+}}, 0($[[R0]]) - -define {i16, i1} @foo(i16* %addr, i16 signext %r, i16 zeroext %new) { - %res = cmpxchg i16* %addr, i16 %r, i16 %new seq_cst seq_cst - ret {i16, i1} %res +define void @foo(i32 %new, i32 %old) { +; O32-LABEL: foo: +; O32: # %bb.0: # %entry +; O32-NEXT: addiu $sp, $sp, -16 +; O32-NEXT: .cfi_def_cfa_offset 16 +; O32-NEXT: move $1, $5 +; O32-NEXT: move $2, $4 +; O32-NEXT: lui $3, %hi(sym) +; O32-NEXT: lw $3, %lo(sym)($3) +; O32-NEXT: sync +; O32-NEXT: lw $6, 12($sp) # 4-byte Folded Reload +; O32-NEXT: $BB0_1: # %entry +; O32-NEXT: # =>This Inner Loop Header: Depth=1 +; O32-NEXT: ll $7, 0($3) +; O32-NEXT: bne $7, $4, $BB0_3 +; O32-NEXT: nop +; O32-NEXT: # %bb.2: # %entry +; O32-NEXT: # in Loop: Header=BB0_1 Depth=1 +; O32-NEXT: move $8, $5 +; O32-NEXT: sc $8, 0($3) +; O32-NEXT: beqz $8, $BB0_1 +; O32-NEXT: nop +; O32-NEXT: $BB0_3: # %entry +; O32-NEXT: sync +; O32-NEXT: sw $7, 12($sp) # 4-byte Folded Spill +; O32-NEXT: sw $6, 8($sp) # 4-byte Folded Spill +; O32-NEXT: sw $1, 4($sp) # 4-byte Folded Spill +; O32-NEXT: sw $2, 0($sp) # 4-byte Folded Spill +; O32-NEXT: addiu $sp, $sp, 16 +; O32-NEXT: jr $ra +; O32-NEXT: nop +; +; N32-LABEL: foo: +; N32: # %bb.0: # %entry +; N32-NEXT: addiu $sp, $sp, -16 +; N32-NEXT: .cfi_def_cfa_offset 16 +; N32-NEXT: move $1, $5 +; N32-NEXT: sll $1, $1, 0 +; N32-NEXT: move $2, $4 +; N32-NEXT: sll $2, $2, 0 +; N32-NEXT: lui $3, %hi(sym) +; N32-NEXT: lw $3, %lo(sym)($3) +; N32-NEXT: sync +; N32-NEXT: lw $6, 12($sp) # 4-byte Folded Reload +; N32-NEXT: .LBB0_1: # %entry +; N32-NEXT: # =>This Inner Loop Header: Depth=1 +; N32-NEXT: ll $7, 0($3) +; N32-NEXT: bne $7, $2, .LBB0_3 +; N32-NEXT: nop +; N32-NEXT: # %bb.2: # %entry +; N32-NEXT: # in Loop: Header=BB0_1 Depth=1 +; N32-NEXT: move $8, $1 +; N32-NEXT: sc $8, 0($3) +; N32-NEXT: beqz $8, .LBB0_1 +; N32-NEXT: nop +; N32-NEXT: .LBB0_3: # %entry +; N32-NEXT: sync +; N32-NEXT: sw $7, 12($sp) # 4-byte Folded Spill +; N32-NEXT: sw $6, 8($sp) # 4-byte Folded Spill +; N32-NEXT: addiu $sp, $sp, 16 +; N32-NEXT: jr $ra +; N32-NEXT: nop +; +; N64-LABEL: foo: +; N64: # %bb.0: # %entry +; N64-NEXT: daddiu $sp, $sp, -16 +; N64-NEXT: .cfi_def_cfa_offset 16 +; N64-NEXT: move $1, $5 +; N64-NEXT: sll $1, $1, 0 +; N64-NEXT: move $2, $4 +; N64-NEXT: sll $2, $2, 0 +; N64-NEXT: lui $4, %highest(sym) +; N64-NEXT: daddiu $4, $4, %higher(sym) +; N64-NEXT: dsll $4, $4, 16 +; N64-NEXT: daddiu $4, $4, %hi(sym) +; N64-NEXT: dsll $4, $4, 16 +; N64-NEXT: ld $4, %lo(sym)($4) +; N64-NEXT: sync +; N64-NEXT: lw $3, 12($sp) # 4-byte Folded Reload +; N64-NEXT: .LBB0_1: # %entry +; N64-NEXT: # =>This Inner Loop Header: Depth=1 +; N64-NEXT: ll $6, 0($4) +; N64-NEXT: bne $6, $2, .LBB0_3 +; N64-NEXT: nop +; N64-NEXT: # %bb.2: # %entry +; N64-NEXT: # in Loop: Header=BB0_1 Depth=1 +; N64-NEXT: move $7, $1 +; N64-NEXT: sc $7, 0($4) +; N64-NEXT: beqz $7, .LBB0_1 +; N64-NEXT: nop +; N64-NEXT: .LBB0_3: # %entry +; N64-NEXT: sync +; N64-NEXT: sw $6, 12($sp) # 4-byte Folded Spill +; N64-NEXT: sw $3, 8($sp) # 4-byte Folded Spill +; N64-NEXT: daddiu $sp, $sp, 16 +; N64-NEXT: jr $ra +; N64-NEXT: nop +entry: + %0 = load i32 *, i32 ** @sym + cmpxchg i32 * %0, i32 %new, i32 %old seq_cst seq_cst + ret void } Index: llvm/trunk/test/CodeGen/Mips/atomic.ll =================================================================== --- llvm/trunk/test/CodeGen/Mips/atomic.ll (revision 336327) +++ llvm/trunk/test/CodeGen/Mips/atomic.ll (revision 336328) @@ -1,548 +1,7657 @@ -; RUN: llc -march=mipsel --disable-machine-licm -mcpu=mips32 -relocation-model=pic < %s | \ -; RUN: FileCheck %s -check-prefixes=ALL,MIPS32-ANY,NO-SEB-SEH,CHECK-EL,NOT-MICROMIPS -; RUN: llc -march=mipsel --disable-machine-licm -mcpu=mips32r2 -relocation-model=pic -verify-machineinstrs < %s | \ -; RUN: FileCheck %s -check-prefixes=ALL,MIPS32-ANY,HAS-SEB-SEH,CHECK-EL,NOT-MICROMIPS -; RUN: llc -march=mipsel --disable-machine-licm -mcpu=mips32r6 -relocation-model=pic -verify-machineinstrs < %s | \ -; RUN: FileCheck %s -check-prefixes=ALL,MIPS32-ANY,HAS-SEB-SEH,CHECK-EL,MIPSR6 -; RUN: llc -march=mips64el --disable-machine-licm -mcpu=mips4 -relocation-model=pic < %s | \ -; RUN: FileCheck %s -check-prefixes=ALL,MIPS64-ANY,NO-SEB-SEH,CHECK-EL,NOT-MICROMIPS -; RUN: llc -march=mips64el --disable-machine-licm -mcpu=mips64 -relocation-model=pic < %s | \ -; RUN: FileCheck %s -check-prefixes=ALL,MIPS64-ANY,NO-SEB-SEH,CHECK-EL,NOT-MICROMIPS -; RUN: llc -march=mips64el --disable-machine-licm -mcpu=mips64r2 -relocation-model=pic -verify-machineinstrs < %s | \ -; RUN: FileCheck %s -check-prefixes=ALL,MIPS64-ANY,HAS-SEB-SEH,CHECK-EL,NOT-MICROMIPS -; RUN: llc -march=mips64el --disable-machine-licm -mcpu=mips64r6 -relocation-model=pic < %s | \ -; RUN: FileCheck %s -check-prefixes=ALL,MIPS64-ANY,HAS-SEB-SEH,CHECK-EL,MIPSR6 -; RUN: llc -march=mips64 -O0 -mcpu=mips64r6 -relocation-model=pic -verify-machineinstrs < %s | \ -; RUN: FileCheck %s -check-prefixes=ALL-LABEL,MIPS64-ANY,O0 -; RUN: llc -march=mipsel --disable-machine-licm -mcpu=mips32r2 -mattr=micromips -relocation-model=pic < %s | \ -; RUN: FileCheck %s -check-prefixes=ALL,MIPS32-ANY,HAS-SEB-SEH,CHECK-EL,MICROMIPS +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=mipsel-unknown-linux-gnu --disable-machine-licm -mcpu=mips32 -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS32 +; RUN: llc -mtriple=mipsel-unknown-linux-gnu -O0 --disable-machine-licm -mcpu=mips32 -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS32O0 +; RUN: llc -mtriple=mipsel-unknown-linux-gnu --disable-machine-licm -mcpu=mips32r2 -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS32R2 +; RUN: llc -mtriple=mipsel-unknown-linux-gnu --disable-machine-licm -mcpu=mips32r6 -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS32R6 +; RUN: llc -mtriple=mipsel-unknown-linux-gnu -O0 --disable-machine-licm -mcpu=mips32r6 -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS32R6O0 +; RUN: llc -mtriple=mips64el-unknown-linux-gnu --disable-machine-licm -mcpu=mips4 -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS4 +; RUN: llc -mtriple=mips64el-unknown-linux-gnu --disable-machine-licm -mcpu=mips64 -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS64 +; RUN: llc -mtriple=mips64el-unknown-linux-gnu --disable-machine-licm -mcpu=mips64r2 -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS64R2 +; RUN: llc -mtriple=mips64el-unknown-linux-gnu --disable-machine-licm -mcpu=mips64r6 -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS64R6 +; RUN: llc -mtriple=mips64-unknown-linux-gnu -O0 -mcpu=mips64r6 -relocation-model=pic -verify-machineinstrs -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS64R6O0 +; RUN: llc -mtriple=mipsel-unknown-linux-gnu --disable-machine-licm -mcpu=mips32r2 -mattr=micromips -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MM32 + +; We want to verify the produced code is well formed all optimization levels, the rest of the tests which ensure correctness. +; RUN: llc -mtriple=mipsel-unknown-linux-gnu -O1 --disable-machine-licm -mcpu=mips32 -relocation-model=pic -verify-machineinstrs < %s | FileCheck %s --check-prefix=O1 +; RUN: llc -mtriple=mipsel-unknown-linux-gnu -O2 --disable-machine-licm -mcpu=mips32 -relocation-model=pic -verify-machineinstrs < %s | FileCheck %s --check-prefix=O2 +; RUN: llc -mtriple=mipsel-unknown-linux-gnu -O3 --disable-machine-licm -mcpu=mips32 -relocation-model=pic -verify-machineinstrs < %s | FileCheck %s --check-prefix=O3 ; Keep one big-endian check so that we don't reduce testing, but don't add more ; since endianness doesn't affect the body of the atomic operations. -; RUN: llc -march=mips --disable-machine-licm -mcpu=mips32 -relocation-model=pic < %s | \ -; RUN: FileCheck %s -check-prefixes=ALL,MIPS32-ANY,NO-SEB-SEH,CHECK-EB,NOT-MICROMIPS +; RUN: llc -mtriple=mips-unknown-linux-gnu --disable-machine-licm -mcpu=mips32 -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS32EB @x = common global i32 0, align 4 define i32 @AtomicLoadAdd32(i32 signext %incr) nounwind { +; MIPS32-LABEL: AtomicLoadAdd32: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: lw $1, %got(x)($1) +; MIPS32-NEXT: $BB0_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $2, 0($1) +; MIPS32-NEXT: addu $3, $2, $4 +; MIPS32-NEXT: sc $3, 0($1) +; MIPS32-NEXT: beqz $3, $BB0_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +; +; MIPS32O0-LABEL: AtomicLoadAdd32: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: lw $2, %got(x)($2) +; MIPS32O0-NEXT: $BB0_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $25, 0($2) +; MIPS32O0-NEXT: addu $1, $25, $4 +; MIPS32O0-NEXT: sc $1, 0($2) +; MIPS32O0-NEXT: beqz $1, $BB0_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: move $2, $25 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicLoadAdd32: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: lw $1, %got(x)($1) +; MIPS32R2-NEXT: $BB0_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $2, 0($1) +; MIPS32R2-NEXT: addu $3, $2, $4 +; MIPS32R2-NEXT: sc $3, 0($1) +; MIPS32R2-NEXT: beqz $3, $BB0_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: nop +; +; MIPS32R6-LABEL: AtomicLoadAdd32: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: lw $1, %got(x)($1) +; MIPS32R6-NEXT: $BB0_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $2, 0($1) +; MIPS32R6-NEXT: addu $3, $2, $4 +; MIPS32R6-NEXT: sc $3, 0($1) +; MIPS32R6-NEXT: beqzc $3, $BB0_1 +; MIPS32R6-NEXT: nop +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: jrc $ra +; +; MIPS32R6O0-LABEL: AtomicLoadAdd32: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -8 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $4 +; MIPS32R6O0-NEXT: lw $2, %got(x)($2) +; MIPS32R6O0-NEXT: $BB0_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $1, 0($2) +; MIPS32R6O0-NEXT: addu $3, $1, $4 +; MIPS32R6O0-NEXT: sc $3, 0($2) +; MIPS32R6O0-NEXT: beqzc $3, $BB0_1 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: move $2, $1 +; MIPS32R6O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: addiu $sp, $sp, 8 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicLoadAdd32: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd32))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd32))) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB0_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $2, 0($1) +; MIPS4-NEXT: addu $3, $2, $4 +; MIPS4-NEXT: sc $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB0_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: AtomicLoadAdd32: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd32))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd32))) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB0_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $2, 0($1) +; MIPS64-NEXT: addu $3, $2, $4 +; MIPS64-NEXT: sc $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB0_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: AtomicLoadAdd32: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd32))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd32))) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB0_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $2, 0($1) +; MIPS64R2-NEXT: addu $3, $2, $4 +; MIPS64R2-NEXT: sc $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB0_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: AtomicLoadAdd32: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd32))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd32))) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB0_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $2, 0($1) +; MIPS64R6-NEXT: addu $3, $2, $4 +; MIPS64R6-NEXT: sc $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB0_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: AtomicLoadAdd32: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd32))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd32))) +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: .LBB0_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $3, 0($1) +; MIPS64R6O0-NEXT: addu $5, $3, $2 +; MIPS64R6O0-NEXT: sc $5, 0($1) +; MIPS64R6O0-NEXT: beqzc $5, .LBB0_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: move $2, $3 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicLoadAdd32: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: lw $1, %got(x)($2) +; MM32-NEXT: $BB0_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $2, 0($1) +; MM32-NEXT: addu16 $3, $2, $4 +; MM32-NEXT: sc $3, 0($1) +; MM32-NEXT: beqzc $3, $BB0_1 +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: jrc $ra +; +; O1-LABEL: AtomicLoadAdd32: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: lw $1, %got(x)($1) +; O1-NEXT: $BB0_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $2, 0($1) +; O1-NEXT: addu $3, $2, $4 +; O1-NEXT: sc $3, 0($1) +; O1-NEXT: beqz $3, $BB0_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: AtomicLoadAdd32: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: lw $1, %got(x)($1) +; O2-NEXT: $BB0_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $2, 0($1) +; O2-NEXT: addu $3, $2, $4 +; O2-NEXT: sc $3, 0($1) +; O2-NEXT: beqz $3, $BB0_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: AtomicLoadAdd32: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: lw $1, %got(x)($1) +; O3-NEXT: $BB0_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $2, 0($1) +; O3-NEXT: addu $3, $2, $4 +; O3-NEXT: sc $3, 0($1) +; O3-NEXT: beqz $3, $BB0_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: nop +; +; MIPS32EB-LABEL: AtomicLoadAdd32: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: lw $1, %got(x)($1) +; MIPS32EB-NEXT: $BB0_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $2, 0($1) +; MIPS32EB-NEXT: addu $3, $2, $4 +; MIPS32EB-NEXT: sc $3, 0($1) +; MIPS32EB-NEXT: beqz $3, $BB0_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: nop entry: %0 = atomicrmw add i32* @x, i32 %incr monotonic ret i32 %0 -; ALL-LABEL: AtomicLoadAdd32: +} -; MIPS32-ANY: lw $[[R0:[0-9]+]], %got(x) -; MIPS64-ANY: ld $[[R0:[0-9]+]], %got_disp(x)( +define i32 @AtomicLoadSub32(i32 signext %incr) nounwind { +; MIPS32-LABEL: AtomicLoadSub32: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: lw $1, %got(x)($1) +; MIPS32-NEXT: $BB1_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $2, 0($1) +; MIPS32-NEXT: subu $3, $2, $4 +; MIPS32-NEXT: sc $3, 0($1) +; MIPS32-NEXT: beqz $3, $BB1_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +; +; MIPS32O0-LABEL: AtomicLoadSub32: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: lw $2, %got(x)($2) +; MIPS32O0-NEXT: $BB1_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $25, 0($2) +; MIPS32O0-NEXT: subu $1, $25, $4 +; MIPS32O0-NEXT: sc $1, 0($2) +; MIPS32O0-NEXT: beqz $1, $BB1_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: move $2, $25 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicLoadSub32: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: lw $1, %got(x)($1) +; MIPS32R2-NEXT: $BB1_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $2, 0($1) +; MIPS32R2-NEXT: subu $3, $2, $4 +; MIPS32R2-NEXT: sc $3, 0($1) +; MIPS32R2-NEXT: beqz $3, $BB1_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: nop +; +; MIPS32R6-LABEL: AtomicLoadSub32: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: lw $1, %got(x)($1) +; MIPS32R6-NEXT: $BB1_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $2, 0($1) +; MIPS32R6-NEXT: subu $3, $2, $4 +; MIPS32R6-NEXT: sc $3, 0($1) +; MIPS32R6-NEXT: beqzc $3, $BB1_1 +; MIPS32R6-NEXT: nop +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: jrc $ra +; +; MIPS32R6O0-LABEL: AtomicLoadSub32: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -8 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $4 +; MIPS32R6O0-NEXT: lw $2, %got(x)($2) +; MIPS32R6O0-NEXT: $BB1_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $1, 0($2) +; MIPS32R6O0-NEXT: subu $3, $1, $4 +; MIPS32R6O0-NEXT: sc $3, 0($2) +; MIPS32R6O0-NEXT: beqzc $3, $BB1_1 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: move $2, $1 +; MIPS32R6O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: addiu $sp, $sp, 8 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicLoadSub32: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub32))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub32))) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB1_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $2, 0($1) +; MIPS4-NEXT: subu $3, $2, $4 +; MIPS4-NEXT: sc $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB1_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: AtomicLoadSub32: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub32))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub32))) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB1_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $2, 0($1) +; MIPS64-NEXT: subu $3, $2, $4 +; MIPS64-NEXT: sc $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB1_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: AtomicLoadSub32: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub32))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub32))) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB1_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $2, 0($1) +; MIPS64R2-NEXT: subu $3, $2, $4 +; MIPS64R2-NEXT: sc $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB1_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: AtomicLoadSub32: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub32))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub32))) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB1_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $2, 0($1) +; MIPS64R6-NEXT: subu $3, $2, $4 +; MIPS64R6-NEXT: sc $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB1_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: AtomicLoadSub32: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub32))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub32))) +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: .LBB1_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $3, 0($1) +; MIPS64R6O0-NEXT: subu $5, $3, $2 +; MIPS64R6O0-NEXT: sc $5, 0($1) +; MIPS64R6O0-NEXT: beqzc $5, .LBB1_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: move $2, $3 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicLoadSub32: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: lw $1, %got(x)($2) +; MM32-NEXT: $BB1_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $2, 0($1) +; MM32-NEXT: subu16 $3, $2, $4 +; MM32-NEXT: sc $3, 0($1) +; MM32-NEXT: beqzc $3, $BB1_1 +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: jrc $ra +; +; O1-LABEL: AtomicLoadSub32: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: lw $1, %got(x)($1) +; O1-NEXT: $BB1_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $2, 0($1) +; O1-NEXT: subu $3, $2, $4 +; O1-NEXT: sc $3, 0($1) +; O1-NEXT: beqz $3, $BB1_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: AtomicLoadSub32: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: lw $1, %got(x)($1) +; O2-NEXT: $BB1_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $2, 0($1) +; O2-NEXT: subu $3, $2, $4 +; O2-NEXT: sc $3, 0($1) +; O2-NEXT: beqz $3, $BB1_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: AtomicLoadSub32: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: lw $1, %got(x)($1) +; O3-NEXT: $BB1_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $2, 0($1) +; O3-NEXT: subu $3, $2, $4 +; O3-NEXT: sc $3, 0($1) +; O3-NEXT: beqz $3, $BB1_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: nop +; +; MIPS32EB-LABEL: AtomicLoadSub32: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: lw $1, %got(x)($1) +; MIPS32EB-NEXT: $BB1_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $2, 0($1) +; MIPS32EB-NEXT: subu $3, $2, $4 +; MIPS32EB-NEXT: sc $3, 0($1) +; MIPS32EB-NEXT: beqz $3, $BB1_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: nop +entry: + %0 = atomicrmw sub i32* @x, i32 %incr monotonic + ret i32 %0 -; O0: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; O0: ld $[[R1:[0-9]+]] -; O0-NEXT: ll $[[R2:[0-9]+]], 0($[[R1]]) - -; ALL: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; ALL: ll $[[R3:[0-9]+]], 0($[[R0]]) -; ALL: addu $[[R4:[0-9]+]], $[[R3]], $4 -; ALL: sc $[[R4]], 0($[[R0]]) -; NOT-MICROMIPS: beqz $[[R4]], [[BB0]] -; MICROMIPS: beqzc $[[R4]], [[BB0]] -; MIPSR6: beqzc $[[R4]], [[BB0]] } -define i32 @AtomicLoadNand32(i32 signext %incr) nounwind { +define i32 @AtomicLoadXor32(i32 signext %incr) nounwind { +; MIPS32-LABEL: AtomicLoadXor32: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: lw $1, %got(x)($1) +; MIPS32-NEXT: $BB2_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $2, 0($1) +; MIPS32-NEXT: xor $3, $2, $4 +; MIPS32-NEXT: sc $3, 0($1) +; MIPS32-NEXT: beqz $3, $BB2_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +; +; MIPS32O0-LABEL: AtomicLoadXor32: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: lw $2, %got(x)($2) +; MIPS32O0-NEXT: $BB2_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $25, 0($2) +; MIPS32O0-NEXT: xor $1, $25, $4 +; MIPS32O0-NEXT: sc $1, 0($2) +; MIPS32O0-NEXT: beqz $1, $BB2_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: move $2, $25 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicLoadXor32: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: lw $1, %got(x)($1) +; MIPS32R2-NEXT: $BB2_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $2, 0($1) +; MIPS32R2-NEXT: xor $3, $2, $4 +; MIPS32R2-NEXT: sc $3, 0($1) +; MIPS32R2-NEXT: beqz $3, $BB2_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: nop +; +; MIPS32R6-LABEL: AtomicLoadXor32: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: lw $1, %got(x)($1) +; MIPS32R6-NEXT: $BB2_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $2, 0($1) +; MIPS32R6-NEXT: xor $3, $2, $4 +; MIPS32R6-NEXT: sc $3, 0($1) +; MIPS32R6-NEXT: beqzc $3, $BB2_1 +; MIPS32R6-NEXT: nop +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: jrc $ra +; +; MIPS32R6O0-LABEL: AtomicLoadXor32: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -8 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $4 +; MIPS32R6O0-NEXT: lw $2, %got(x)($2) +; MIPS32R6O0-NEXT: $BB2_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $1, 0($2) +; MIPS32R6O0-NEXT: xor $3, $1, $4 +; MIPS32R6O0-NEXT: sc $3, 0($2) +; MIPS32R6O0-NEXT: beqzc $3, $BB2_1 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: move $2, $1 +; MIPS32R6O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: addiu $sp, $sp, 8 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicLoadXor32: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadXor32))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadXor32))) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB2_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $2, 0($1) +; MIPS4-NEXT: xor $3, $2, $4 +; MIPS4-NEXT: sc $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB2_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: AtomicLoadXor32: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadXor32))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadXor32))) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB2_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $2, 0($1) +; MIPS64-NEXT: xor $3, $2, $4 +; MIPS64-NEXT: sc $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB2_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: AtomicLoadXor32: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadXor32))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadXor32))) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB2_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $2, 0($1) +; MIPS64R2-NEXT: xor $3, $2, $4 +; MIPS64R2-NEXT: sc $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB2_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: AtomicLoadXor32: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadXor32))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadXor32))) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB2_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $2, 0($1) +; MIPS64R6-NEXT: xor $3, $2, $4 +; MIPS64R6-NEXT: sc $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB2_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: AtomicLoadXor32: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadXor32))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadXor32))) +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: .LBB2_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $3, 0($1) +; MIPS64R6O0-NEXT: xor $5, $3, $2 +; MIPS64R6O0-NEXT: sc $5, 0($1) +; MIPS64R6O0-NEXT: beqzc $5, .LBB2_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: move $2, $3 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicLoadXor32: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: lw $1, %got(x)($2) +; MM32-NEXT: $BB2_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $2, 0($1) +; MM32-NEXT: xor $3, $2, $4 +; MM32-NEXT: sc $3, 0($1) +; MM32-NEXT: beqzc $3, $BB2_1 +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: jrc $ra +; +; O1-LABEL: AtomicLoadXor32: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: lw $1, %got(x)($1) +; O1-NEXT: $BB2_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $2, 0($1) +; O1-NEXT: xor $3, $2, $4 +; O1-NEXT: sc $3, 0($1) +; O1-NEXT: beqz $3, $BB2_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: AtomicLoadXor32: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: lw $1, %got(x)($1) +; O2-NEXT: $BB2_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $2, 0($1) +; O2-NEXT: xor $3, $2, $4 +; O2-NEXT: sc $3, 0($1) +; O2-NEXT: beqz $3, $BB2_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: AtomicLoadXor32: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: lw $1, %got(x)($1) +; O3-NEXT: $BB2_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $2, 0($1) +; O3-NEXT: xor $3, $2, $4 +; O3-NEXT: sc $3, 0($1) +; O3-NEXT: beqz $3, $BB2_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: nop +; +; MIPS32EB-LABEL: AtomicLoadXor32: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: lw $1, %got(x)($1) +; MIPS32EB-NEXT: $BB2_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $2, 0($1) +; MIPS32EB-NEXT: xor $3, $2, $4 +; MIPS32EB-NEXT: sc $3, 0($1) +; MIPS32EB-NEXT: beqz $3, $BB2_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: nop entry: - %0 = atomicrmw nand i32* @x, i32 %incr monotonic + %0 = atomicrmw xor i32* @x, i32 %incr monotonic ret i32 %0 +} -; ALL-LABEL: AtomicLoadNand32: - -; MIPS32-ANY: lw $[[R0:[0-9]+]], %got(x) -; MIPS64-ANY: ld $[[R0:[0-9]+]], %got_disp(x)( +define i32 @AtomicLoadOr32(i32 signext %incr) nounwind { +; MIPS32-LABEL: AtomicLoadOr32: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: lw $1, %got(x)($1) +; MIPS32-NEXT: $BB3_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $2, 0($1) +; MIPS32-NEXT: or $3, $2, $4 +; MIPS32-NEXT: sc $3, 0($1) +; MIPS32-NEXT: beqz $3, $BB3_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +; +; MIPS32O0-LABEL: AtomicLoadOr32: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: lw $2, %got(x)($2) +; MIPS32O0-NEXT: $BB3_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $25, 0($2) +; MIPS32O0-NEXT: or $1, $25, $4 +; MIPS32O0-NEXT: sc $1, 0($2) +; MIPS32O0-NEXT: beqz $1, $BB3_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: move $2, $25 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicLoadOr32: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: lw $1, %got(x)($1) +; MIPS32R2-NEXT: $BB3_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $2, 0($1) +; MIPS32R2-NEXT: or $3, $2, $4 +; MIPS32R2-NEXT: sc $3, 0($1) +; MIPS32R2-NEXT: beqz $3, $BB3_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: nop +; +; MIPS32R6-LABEL: AtomicLoadOr32: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: lw $1, %got(x)($1) +; MIPS32R6-NEXT: $BB3_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $2, 0($1) +; MIPS32R6-NEXT: or $3, $2, $4 +; MIPS32R6-NEXT: sc $3, 0($1) +; MIPS32R6-NEXT: beqzc $3, $BB3_1 +; MIPS32R6-NEXT: nop +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: jrc $ra +; +; MIPS32R6O0-LABEL: AtomicLoadOr32: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -8 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $4 +; MIPS32R6O0-NEXT: lw $2, %got(x)($2) +; MIPS32R6O0-NEXT: $BB3_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $1, 0($2) +; MIPS32R6O0-NEXT: or $3, $1, $4 +; MIPS32R6O0-NEXT: sc $3, 0($2) +; MIPS32R6O0-NEXT: beqzc $3, $BB3_1 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: move $2, $1 +; MIPS32R6O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: addiu $sp, $sp, 8 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicLoadOr32: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadOr32))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadOr32))) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB3_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $2, 0($1) +; MIPS4-NEXT: or $3, $2, $4 +; MIPS4-NEXT: sc $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB3_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: AtomicLoadOr32: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadOr32))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadOr32))) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB3_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $2, 0($1) +; MIPS64-NEXT: or $3, $2, $4 +; MIPS64-NEXT: sc $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB3_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: AtomicLoadOr32: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadOr32))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadOr32))) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB3_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $2, 0($1) +; MIPS64R2-NEXT: or $3, $2, $4 +; MIPS64R2-NEXT: sc $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB3_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: AtomicLoadOr32: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadOr32))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadOr32))) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB3_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $2, 0($1) +; MIPS64R6-NEXT: or $3, $2, $4 +; MIPS64R6-NEXT: sc $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB3_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: AtomicLoadOr32: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadOr32))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadOr32))) +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: .LBB3_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $3, 0($1) +; MIPS64R6O0-NEXT: or $5, $3, $2 +; MIPS64R6O0-NEXT: sc $5, 0($1) +; MIPS64R6O0-NEXT: beqzc $5, .LBB3_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: move $2, $3 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicLoadOr32: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: lw $1, %got(x)($2) +; MM32-NEXT: $BB3_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $2, 0($1) +; MM32-NEXT: or $3, $2, $4 +; MM32-NEXT: sc $3, 0($1) +; MM32-NEXT: beqzc $3, $BB3_1 +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: jrc $ra +; +; O1-LABEL: AtomicLoadOr32: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: lw $1, %got(x)($1) +; O1-NEXT: $BB3_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $2, 0($1) +; O1-NEXT: or $3, $2, $4 +; O1-NEXT: sc $3, 0($1) +; O1-NEXT: beqz $3, $BB3_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: AtomicLoadOr32: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: lw $1, %got(x)($1) +; O2-NEXT: $BB3_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $2, 0($1) +; O2-NEXT: or $3, $2, $4 +; O2-NEXT: sc $3, 0($1) +; O2-NEXT: beqz $3, $BB3_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: AtomicLoadOr32: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: lw $1, %got(x)($1) +; O3-NEXT: $BB3_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $2, 0($1) +; O3-NEXT: or $3, $2, $4 +; O3-NEXT: sc $3, 0($1) +; O3-NEXT: beqz $3, $BB3_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: nop +; +; MIPS32EB-LABEL: AtomicLoadOr32: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: lw $1, %got(x)($1) +; MIPS32EB-NEXT: $BB3_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $2, 0($1) +; MIPS32EB-NEXT: or $3, $2, $4 +; MIPS32EB-NEXT: sc $3, 0($1) +; MIPS32EB-NEXT: beqz $3, $BB3_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: nop +entry: + %0 = atomicrmw or i32* @x, i32 %incr monotonic + ret i32 %0 +} +define i32 @AtomicLoadAnd32(i32 signext %incr) nounwind { +; MIPS32-LABEL: AtomicLoadAnd32: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: lw $1, %got(x)($1) +; MIPS32-NEXT: $BB4_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $2, 0($1) +; MIPS32-NEXT: and $3, $2, $4 +; MIPS32-NEXT: sc $3, 0($1) +; MIPS32-NEXT: beqz $3, $BB4_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +; +; MIPS32O0-LABEL: AtomicLoadAnd32: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: lw $2, %got(x)($2) +; MIPS32O0-NEXT: $BB4_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $25, 0($2) +; MIPS32O0-NEXT: and $1, $25, $4 +; MIPS32O0-NEXT: sc $1, 0($2) +; MIPS32O0-NEXT: beqz $1, $BB4_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: move $2, $25 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicLoadAnd32: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: lw $1, %got(x)($1) +; MIPS32R2-NEXT: $BB4_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $2, 0($1) +; MIPS32R2-NEXT: and $3, $2, $4 +; MIPS32R2-NEXT: sc $3, 0($1) +; MIPS32R2-NEXT: beqz $3, $BB4_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: nop +; +; MIPS32R6-LABEL: AtomicLoadAnd32: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: lw $1, %got(x)($1) +; MIPS32R6-NEXT: $BB4_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $2, 0($1) +; MIPS32R6-NEXT: and $3, $2, $4 +; MIPS32R6-NEXT: sc $3, 0($1) +; MIPS32R6-NEXT: beqzc $3, $BB4_1 +; MIPS32R6-NEXT: nop +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: jrc $ra +; +; MIPS32R6O0-LABEL: AtomicLoadAnd32: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -8 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $4 +; MIPS32R6O0-NEXT: lw $2, %got(x)($2) +; MIPS32R6O0-NEXT: $BB4_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $1, 0($2) +; MIPS32R6O0-NEXT: and $3, $1, $4 +; MIPS32R6O0-NEXT: sc $3, 0($2) +; MIPS32R6O0-NEXT: beqzc $3, $BB4_1 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: move $2, $1 +; MIPS32R6O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: addiu $sp, $sp, 8 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicLoadAnd32: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAnd32))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAnd32))) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB4_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $2, 0($1) +; MIPS4-NEXT: and $3, $2, $4 +; MIPS4-NEXT: sc $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB4_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: AtomicLoadAnd32: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAnd32))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAnd32))) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB4_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $2, 0($1) +; MIPS64-NEXT: and $3, $2, $4 +; MIPS64-NEXT: sc $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB4_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: AtomicLoadAnd32: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAnd32))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAnd32))) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB4_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $2, 0($1) +; MIPS64R2-NEXT: and $3, $2, $4 +; MIPS64R2-NEXT: sc $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB4_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: AtomicLoadAnd32: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAnd32))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAnd32))) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB4_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $2, 0($1) +; MIPS64R6-NEXT: and $3, $2, $4 +; MIPS64R6-NEXT: sc $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB4_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: AtomicLoadAnd32: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAnd32))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAnd32))) +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: .LBB4_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $3, 0($1) +; MIPS64R6O0-NEXT: and $5, $3, $2 +; MIPS64R6O0-NEXT: sc $5, 0($1) +; MIPS64R6O0-NEXT: beqzc $5, .LBB4_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: move $2, $3 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicLoadAnd32: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: lw $1, %got(x)($2) +; MM32-NEXT: $BB4_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $2, 0($1) +; MM32-NEXT: and $3, $2, $4 +; MM32-NEXT: sc $3, 0($1) +; MM32-NEXT: beqzc $3, $BB4_1 +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: jrc $ra +; +; O1-LABEL: AtomicLoadAnd32: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: lw $1, %got(x)($1) +; O1-NEXT: $BB4_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $2, 0($1) +; O1-NEXT: and $3, $2, $4 +; O1-NEXT: sc $3, 0($1) +; O1-NEXT: beqz $3, $BB4_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: AtomicLoadAnd32: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: lw $1, %got(x)($1) +; O2-NEXT: $BB4_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $2, 0($1) +; O2-NEXT: and $3, $2, $4 +; O2-NEXT: sc $3, 0($1) +; O2-NEXT: beqz $3, $BB4_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: AtomicLoadAnd32: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: lw $1, %got(x)($1) +; O3-NEXT: $BB4_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $2, 0($1) +; O3-NEXT: and $3, $2, $4 +; O3-NEXT: sc $3, 0($1) +; O3-NEXT: beqz $3, $BB4_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: nop +; +; MIPS32EB-LABEL: AtomicLoadAnd32: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: lw $1, %got(x)($1) +; MIPS32EB-NEXT: $BB4_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $2, 0($1) +; MIPS32EB-NEXT: and $3, $2, $4 +; MIPS32EB-NEXT: sc $3, 0($1) +; MIPS32EB-NEXT: beqz $3, $BB4_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: nop +entry: + %0 = atomicrmw and i32* @x, i32 %incr monotonic + ret i32 %0 +} +define i32 @AtomicLoadNand32(i32 signext %incr) nounwind { +; MIPS32-LABEL: AtomicLoadNand32: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: lw $1, %got(x)($1) +; MIPS32-NEXT: $BB5_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $2, 0($1) +; MIPS32-NEXT: and $3, $2, $4 +; MIPS32-NEXT: nor $3, $zero, $3 +; MIPS32-NEXT: sc $3, 0($1) +; MIPS32-NEXT: beqz $3, $BB5_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +; +; MIPS32O0-LABEL: AtomicLoadNand32: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: lw $2, %got(x)($2) +; MIPS32O0-NEXT: $BB5_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $25, 0($2) +; MIPS32O0-NEXT: and $1, $25, $4 +; MIPS32O0-NEXT: nor $1, $zero, $1 +; MIPS32O0-NEXT: sc $1, 0($2) +; MIPS32O0-NEXT: beqz $1, $BB5_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: move $2, $25 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicLoadNand32: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: lw $1, %got(x)($1) +; MIPS32R2-NEXT: $BB5_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $2, 0($1) +; MIPS32R2-NEXT: and $3, $2, $4 +; MIPS32R2-NEXT: nor $3, $zero, $3 +; MIPS32R2-NEXT: sc $3, 0($1) +; MIPS32R2-NEXT: beqz $3, $BB5_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: nop +; +; MIPS32R6-LABEL: AtomicLoadNand32: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: lw $1, %got(x)($1) +; MIPS32R6-NEXT: $BB5_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $2, 0($1) +; MIPS32R6-NEXT: and $3, $2, $4 +; MIPS32R6-NEXT: nor $3, $zero, $3 +; MIPS32R6-NEXT: sc $3, 0($1) +; MIPS32R6-NEXT: beqzc $3, $BB5_1 +; MIPS32R6-NEXT: nop +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: jrc $ra +; +; MIPS32R6O0-LABEL: AtomicLoadNand32: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -8 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $4 +; MIPS32R6O0-NEXT: lw $2, %got(x)($2) +; MIPS32R6O0-NEXT: $BB5_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $1, 0($2) +; MIPS32R6O0-NEXT: and $3, $1, $4 +; MIPS32R6O0-NEXT: nor $3, $zero, $3 +; MIPS32R6O0-NEXT: sc $3, 0($2) +; MIPS32R6O0-NEXT: beqzc $3, $BB5_1 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: move $2, $1 +; MIPS32R6O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: addiu $sp, $sp, 8 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicLoadNand32: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand32))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand32))) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB5_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $2, 0($1) +; MIPS4-NEXT: and $3, $2, $4 +; MIPS4-NEXT: nor $3, $zero, $3 +; MIPS4-NEXT: sc $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB5_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: AtomicLoadNand32: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand32))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand32))) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB5_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $2, 0($1) +; MIPS64-NEXT: and $3, $2, $4 +; MIPS64-NEXT: nor $3, $zero, $3 +; MIPS64-NEXT: sc $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB5_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: AtomicLoadNand32: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand32))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand32))) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB5_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $2, 0($1) +; MIPS64R2-NEXT: and $3, $2, $4 +; MIPS64R2-NEXT: nor $3, $zero, $3 +; MIPS64R2-NEXT: sc $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB5_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: AtomicLoadNand32: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand32))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand32))) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB5_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $2, 0($1) +; MIPS64R6-NEXT: and $3, $2, $4 +; MIPS64R6-NEXT: nor $3, $zero, $3 +; MIPS64R6-NEXT: sc $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB5_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: AtomicLoadNand32: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand32))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand32))) +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: .LBB5_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $3, 0($1) +; MIPS64R6O0-NEXT: and $5, $3, $2 +; MIPS64R6O0-NEXT: nor $5, $zero, $5 +; MIPS64R6O0-NEXT: sc $5, 0($1) +; MIPS64R6O0-NEXT: beqzc $5, .LBB5_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: move $2, $3 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicLoadNand32: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: lw $1, %got(x)($2) +; MM32-NEXT: $BB5_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $2, 0($1) +; MM32-NEXT: and $3, $2, $4 +; MM32-NEXT: nor $3, $zero, $3 +; MM32-NEXT: sc $3, 0($1) +; MM32-NEXT: beqzc $3, $BB5_1 +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: jrc $ra +; +; O1-LABEL: AtomicLoadNand32: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: lw $1, %got(x)($1) +; O1-NEXT: $BB5_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $2, 0($1) +; O1-NEXT: and $3, $2, $4 +; O1-NEXT: nor $3, $zero, $3 +; O1-NEXT: sc $3, 0($1) +; O1-NEXT: beqz $3, $BB5_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: AtomicLoadNand32: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: lw $1, %got(x)($1) +; O2-NEXT: $BB5_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $2, 0($1) +; O2-NEXT: and $3, $2, $4 +; O2-NEXT: nor $3, $zero, $3 +; O2-NEXT: sc $3, 0($1) +; O2-NEXT: beqz $3, $BB5_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: AtomicLoadNand32: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: lw $1, %got(x)($1) +; O3-NEXT: $BB5_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $2, 0($1) +; O3-NEXT: and $3, $2, $4 +; O3-NEXT: nor $3, $zero, $3 +; O3-NEXT: sc $3, 0($1) +; O3-NEXT: beqz $3, $BB5_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: nop +; +; MIPS32EB-LABEL: AtomicLoadNand32: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: lw $1, %got(x)($1) +; MIPS32EB-NEXT: $BB5_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $2, 0($1) +; MIPS32EB-NEXT: and $3, $2, $4 +; MIPS32EB-NEXT: nor $3, $zero, $3 +; MIPS32EB-NEXT: sc $3, 0($1) +; MIPS32EB-NEXT: beqz $3, $BB5_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: nop +entry: + %0 = atomicrmw nand i32* @x, i32 %incr monotonic + ret i32 %0 -; ALL: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; ALL: ll $[[R1:[0-9]+]], 0($[[R0]]) -; ALL: and $[[R3:[0-9]+]], $[[R1]], $4 -; ALL: nor $[[R2:[0-9]+]], $zero, $[[R3]] -; ALL: sc $[[R2]], 0($[[R0]]) -; NOT-MICROMIPS: beqz $[[R2]], [[BB0]] -; MICROMIPS: beqzc $[[R2]], [[BB0]] -; MIPSR6: beqzc $[[R2]], [[BB0]] } define i32 @AtomicSwap32(i32 signext %newval) nounwind { +; MIPS32-LABEL: AtomicSwap32: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addiu $sp, $sp, -8 +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: sw $4, 4($sp) +; MIPS32-NEXT: lw $1, %got(x)($1) +; MIPS32-NEXT: $BB6_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $2, 0($1) +; MIPS32-NEXT: move $3, $4 +; MIPS32-NEXT: sc $3, 0($1) +; MIPS32-NEXT: beqz $3, $BB6_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: addiu $sp, $sp, 8 +; +; MIPS32O0-LABEL: AtomicSwap32: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addiu $sp, $sp, -8 +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: sw $4, 4($sp) +; MIPS32O0-NEXT: lw $4, 4($sp) +; MIPS32O0-NEXT: lw $2, %got(x)($2) +; MIPS32O0-NEXT: $BB6_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $25, 0($2) +; MIPS32O0-NEXT: move $1, $4 +; MIPS32O0-NEXT: sc $1, 0($2) +; MIPS32O0-NEXT: beqz $1, $BB6_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: move $2, $25 +; MIPS32O0-NEXT: addiu $sp, $sp, 8 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicSwap32: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addiu $sp, $sp, -8 +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: sw $4, 4($sp) +; MIPS32R2-NEXT: lw $1, %got(x)($1) +; MIPS32R2-NEXT: $BB6_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $2, 0($1) +; MIPS32R2-NEXT: move $3, $4 +; MIPS32R2-NEXT: sc $3, 0($1) +; MIPS32R2-NEXT: beqz $3, $BB6_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: addiu $sp, $sp, 8 +; +; MIPS32R6-LABEL: AtomicSwap32: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addiu $sp, $sp, -8 +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: sw $4, 4($sp) +; MIPS32R6-NEXT: lw $1, %got(x)($1) +; MIPS32R6-NEXT: $BB6_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $2, 0($1) +; MIPS32R6-NEXT: move $3, $4 +; MIPS32R6-NEXT: sc $3, 0($1) +; MIPS32R6-NEXT: beqzc $3, $BB6_1 +; MIPS32R6-NEXT: nop +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: jr $ra +; MIPS32R6-NEXT: addiu $sp, $sp, 8 +; +; MIPS32R6O0-LABEL: AtomicSwap32: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -8 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $4 +; MIPS32R6O0-NEXT: sw $4, 4($sp) +; MIPS32R6O0-NEXT: lw $4, 4($sp) +; MIPS32R6O0-NEXT: lw $2, %got(x)($2) +; MIPS32R6O0-NEXT: $BB6_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $1, 0($2) +; MIPS32R6O0-NEXT: move $3, $4 +; MIPS32R6O0-NEXT: sc $3, 0($2) +; MIPS32R6O0-NEXT: beqzc $3, $BB6_1 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: move $2, $1 +; MIPS32R6O0-NEXT: sw $25, 0($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: addiu $sp, $sp, 8 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicSwap32: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: daddiu $sp, $sp, -16 +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap32))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap32))) +; MIPS4-NEXT: sw $4, 12($sp) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB6_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $2, 0($1) +; MIPS4-NEXT: move $3, $4 +; MIPS4-NEXT: sc $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB6_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64-LABEL: AtomicSwap32: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: daddiu $sp, $sp, -16 +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap32))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap32))) +; MIPS64-NEXT: sw $4, 12($sp) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB6_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $2, 0($1) +; MIPS64-NEXT: move $3, $4 +; MIPS64-NEXT: sc $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB6_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64R2-LABEL: AtomicSwap32: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: daddiu $sp, $sp, -16 +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap32))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap32))) +; MIPS64R2-NEXT: sw $4, 12($sp) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB6_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $2, 0($1) +; MIPS64R2-NEXT: move $3, $4 +; MIPS64R2-NEXT: sc $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB6_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64R6-LABEL: AtomicSwap32: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap32))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap32))) +; MIPS64R6-NEXT: sw $4, 12($sp) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB6_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $2, 0($1) +; MIPS64R6-NEXT: move $3, $4 +; MIPS64R6-NEXT: sc $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB6_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: jr $ra +; MIPS64R6-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64R6O0-LABEL: AtomicSwap32: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap32))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap32))) +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: sw $2, 12($sp) +; MIPS64R6O0-NEXT: lw $2, 12($sp) +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: .LBB6_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $3, 0($1) +; MIPS64R6O0-NEXT: move $5, $2 +; MIPS64R6O0-NEXT: sc $5, 0($1) +; MIPS64R6O0-NEXT: beqzc $5, .LBB6_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: move $2, $3 +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicSwap32: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addiu $sp, $sp, -8 +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: sw $4, 4($sp) +; MM32-NEXT: lw $1, %got(x)($2) +; MM32-NEXT: $BB6_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $2, 0($1) +; MM32-NEXT: move $3, $4 +; MM32-NEXT: sc $3, 0($1) +; MM32-NEXT: beqzc $3, $BB6_1 +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: addiusp 8 +; MM32-NEXT: jrc $ra +; +; O1-LABEL: AtomicSwap32: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addiu $sp, $sp, -8 +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: sw $4, 4($sp) +; O1-NEXT: lw $1, %got(x)($1) +; O1-NEXT: $BB6_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $2, 0($1) +; O1-NEXT: move $3, $4 +; O1-NEXT: sc $3, 0($1) +; O1-NEXT: beqz $3, $BB6_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: addiu $sp, $sp, 8 +; +; O2-LABEL: AtomicSwap32: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addiu $sp, $sp, -8 +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: sw $4, 4($sp) +; O2-NEXT: lw $1, %got(x)($1) +; O2-NEXT: $BB6_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $2, 0($1) +; O2-NEXT: move $3, $4 +; O2-NEXT: sc $3, 0($1) +; O2-NEXT: beqz $3, $BB6_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: addiu $sp, $sp, 8 +; +; O3-LABEL: AtomicSwap32: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addiu $sp, $sp, -8 +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: sw $4, 4($sp) +; O3-NEXT: lw $1, %got(x)($1) +; O3-NEXT: $BB6_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $2, 0($1) +; O3-NEXT: move $3, $4 +; O3-NEXT: sc $3, 0($1) +; O3-NEXT: beqz $3, $BB6_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: addiu $sp, $sp, 8 +; +; MIPS32EB-LABEL: AtomicSwap32: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addiu $sp, $sp, -8 +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: sw $4, 4($sp) +; MIPS32EB-NEXT: lw $1, %got(x)($1) +; MIPS32EB-NEXT: $BB6_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $2, 0($1) +; MIPS32EB-NEXT: move $3, $4 +; MIPS32EB-NEXT: sc $3, 0($1) +; MIPS32EB-NEXT: beqz $3, $BB6_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: addiu $sp, $sp, 8 entry: %newval.addr = alloca i32, align 4 store i32 %newval, i32* %newval.addr, align 4 %tmp = load i32, i32* %newval.addr, align 4 %0 = atomicrmw xchg i32* @x, i32 %tmp monotonic ret i32 %0 -; ALL-LABEL: AtomicSwap32: - -; MIPS32-ANY: lw $[[R0:[0-9]+]], %got(x) -; MIPS64-ANY: ld $[[R0:[0-9]+]], %got_disp(x) - -; ALL: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; ALL: ll ${{[0-9]+}}, 0($[[R0]]) -; ALL: sc $[[R2:[0-9]+]], 0($[[R0]]) -; NOT-MICROMIPS: beqz $[[R2]], [[BB0]] -; MICROMIPS: beqzc $[[R2]], [[BB0]] -; MIPSR6: beqzc $[[R2]], [[BB0]] } define i32 @AtomicCmpSwap32(i32 signext %oldval, i32 signext %newval) nounwind { +; MIPS32-LABEL: AtomicCmpSwap32: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addiu $sp, $sp, -8 +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: sw $5, 4($sp) +; MIPS32-NEXT: lw $1, %got(x)($1) +; MIPS32-NEXT: $BB7_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $2, 0($1) +; MIPS32-NEXT: bne $2, $4, $BB7_3 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS32-NEXT: move $3, $5 +; MIPS32-NEXT: sc $3, 0($1) +; MIPS32-NEXT: beqz $3, $BB7_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: $BB7_3: # %entry +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: addiu $sp, $sp, 8 +; +; MIPS32O0-LABEL: AtomicCmpSwap32: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addiu $sp, $sp, -16 +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: sw $5, 12($sp) +; MIPS32O0-NEXT: lw $5, 12($sp) +; MIPS32O0-NEXT: lw $2, %got(x)($2) +; MIPS32O0-NEXT: lw $25, 8($sp) # 4-byte Folded Reload +; MIPS32O0-NEXT: move $1, $4 +; MIPS32O0-NEXT: $BB7_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $3, 0($2) +; MIPS32O0-NEXT: bne $3, $1, $BB7_3 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS32O0-NEXT: move $6, $5 +; MIPS32O0-NEXT: sc $6, 0($2) +; MIPS32O0-NEXT: beqz $6, $BB7_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: $BB7_3: # %entry +; MIPS32O0-NEXT: xor $1, $3, $4 +; MIPS32O0-NEXT: sltiu $1, $1, 1 +; MIPS32O0-NEXT: move $2, $3 +; MIPS32O0-NEXT: sw $3, 8($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: sw $1, 0($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: addiu $sp, $sp, 16 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicCmpSwap32: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addiu $sp, $sp, -8 +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: sw $5, 4($sp) +; MIPS32R2-NEXT: lw $1, %got(x)($1) +; MIPS32R2-NEXT: $BB7_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $2, 0($1) +; MIPS32R2-NEXT: bne $2, $4, $BB7_3 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS32R2-NEXT: move $3, $5 +; MIPS32R2-NEXT: sc $3, 0($1) +; MIPS32R2-NEXT: beqz $3, $BB7_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: $BB7_3: # %entry +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: addiu $sp, $sp, 8 +; +; MIPS32R6-LABEL: AtomicCmpSwap32: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addiu $sp, $sp, -8 +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: sw $5, 4($sp) +; MIPS32R6-NEXT: lw $1, %got(x)($1) +; MIPS32R6-NEXT: $BB7_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $2, 0($1) +; MIPS32R6-NEXT: bnec $2, $4, $BB7_3 +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS32R6-NEXT: move $3, $5 +; MIPS32R6-NEXT: sc $3, 0($1) +; MIPS32R6-NEXT: beqzc $3, $BB7_1 +; MIPS32R6-NEXT: nop +; MIPS32R6-NEXT: $BB7_3: # %entry +; MIPS32R6-NEXT: jr $ra +; MIPS32R6-NEXT: addiu $sp, $sp, 8 +; +; MIPS32R6O0-LABEL: AtomicCmpSwap32: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -24 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $5 +; MIPS32R6O0-NEXT: move $1, $4 +; MIPS32R6O0-NEXT: sw $5, 20($sp) +; MIPS32R6O0-NEXT: lw $5, 20($sp) +; MIPS32R6O0-NEXT: lw $2, %got(x)($2) +; MIPS32R6O0-NEXT: lw $3, 16($sp) # 4-byte Folded Reload +; MIPS32R6O0-NEXT: $BB7_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $6, 0($2) +; MIPS32R6O0-NEXT: bnec $6, $4, $BB7_3 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS32R6O0-NEXT: move $7, $5 +; MIPS32R6O0-NEXT: sc $7, 0($2) +; MIPS32R6O0-NEXT: beqzc $7, $BB7_1 +; MIPS32R6O0-NEXT: $BB7_3: # %entry +; MIPS32R6O0-NEXT: move $2, $6 +; MIPS32R6O0-NEXT: sw $6, 16($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $1, 12($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $3, 8($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: addiu $sp, $sp, 24 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicCmpSwap32: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: daddiu $sp, $sp, -16 +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap32))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap32))) +; MIPS4-NEXT: sw $5, 12($sp) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB7_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $2, 0($1) +; MIPS4-NEXT: bne $2, $4, .LBB7_3 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS4-NEXT: move $3, $5 +; MIPS4-NEXT: sc $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB7_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: .LBB7_3: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64-LABEL: AtomicCmpSwap32: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: daddiu $sp, $sp, -16 +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap32))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap32))) +; MIPS64-NEXT: sw $5, 12($sp) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB7_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $2, 0($1) +; MIPS64-NEXT: bne $2, $4, .LBB7_3 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS64-NEXT: move $3, $5 +; MIPS64-NEXT: sc $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB7_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: .LBB7_3: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64R2-LABEL: AtomicCmpSwap32: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: daddiu $sp, $sp, -16 +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap32))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap32))) +; MIPS64R2-NEXT: sw $5, 12($sp) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB7_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $2, 0($1) +; MIPS64R2-NEXT: bne $2, $4, .LBB7_3 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS64R2-NEXT: move $3, $5 +; MIPS64R2-NEXT: sc $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB7_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: .LBB7_3: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64R6-LABEL: AtomicCmpSwap32: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap32))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap32))) +; MIPS64R6-NEXT: sw $5, 12($sp) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB7_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $2, 0($1) +; MIPS64R6-NEXT: bnec $2, $4, .LBB7_3 +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS64R6-NEXT: move $3, $5 +; MIPS64R6-NEXT: sc $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB7_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: .LBB7_3: # %entry +; MIPS64R6-NEXT: jr $ra +; MIPS64R6-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64R6O0-LABEL: AtomicCmpSwap32: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap32))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap32))) +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: move $3, $5 +; MIPS64R6O0-NEXT: sw $3, 12($sp) +; MIPS64R6O0-NEXT: lw $3, 12($sp) +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: lw $6, 8($sp) # 4-byte Folded Reload +; MIPS64R6O0-NEXT: .LBB7_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $7, 0($1) +; MIPS64R6O0-NEXT: bnec $7, $2, .LBB7_3 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS64R6O0-NEXT: move $8, $3 +; MIPS64R6O0-NEXT: sc $8, 0($1) +; MIPS64R6O0-NEXT: beqzc $8, .LBB7_1 +; MIPS64R6O0-NEXT: .LBB7_3: # %entry +; MIPS64R6O0-NEXT: move $2, $7 +; MIPS64R6O0-NEXT: sw $7, 8($sp) # 4-byte Folded Spill +; MIPS64R6O0-NEXT: sw $6, 4($sp) # 4-byte Folded Spill +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicCmpSwap32: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addiu $sp, $sp, -8 +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: sw $5, 4($sp) +; MM32-NEXT: lw $1, %got(x)($2) +; MM32-NEXT: $BB7_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $2, 0($1) +; MM32-NEXT: bne $2, $4, $BB7_3 +; MM32-NEXT: nop +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MM32-NEXT: move $3, $5 +; MM32-NEXT: sc $3, 0($1) +; MM32-NEXT: beqzc $3, $BB7_1 +; MM32-NEXT: $BB7_3: # %entry +; MM32-NEXT: addiusp 8 +; MM32-NEXT: jrc $ra +; +; O1-LABEL: AtomicCmpSwap32: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addiu $sp, $sp, -8 +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: sw $5, 4($sp) +; O1-NEXT: lw $1, %got(x)($1) +; O1-NEXT: $BB7_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $2, 0($1) +; O1-NEXT: bne $2, $4, $BB7_3 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: # in Loop: Header=BB7_1 Depth=1 +; O1-NEXT: move $3, $5 +; O1-NEXT: sc $3, 0($1) +; O1-NEXT: beqz $3, $BB7_1 +; O1-NEXT: nop +; O1-NEXT: $BB7_3: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: addiu $sp, $sp, 8 +; +; O2-LABEL: AtomicCmpSwap32: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addiu $sp, $sp, -8 +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: sw $5, 4($sp) +; O2-NEXT: lw $1, %got(x)($1) +; O2-NEXT: $BB7_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $2, 0($1) +; O2-NEXT: bne $2, $4, $BB7_3 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: # in Loop: Header=BB7_1 Depth=1 +; O2-NEXT: move $3, $5 +; O2-NEXT: sc $3, 0($1) +; O2-NEXT: beqz $3, $BB7_1 +; O2-NEXT: nop +; O2-NEXT: $BB7_3: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: addiu $sp, $sp, 8 +; +; O3-LABEL: AtomicCmpSwap32: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addiu $sp, $sp, -8 +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: sw $5, 4($sp) +; O3-NEXT: lw $1, %got(x)($1) +; O3-NEXT: $BB7_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $2, 0($1) +; O3-NEXT: bne $2, $4, $BB7_3 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: # in Loop: Header=BB7_1 Depth=1 +; O3-NEXT: move $3, $5 +; O3-NEXT: sc $3, 0($1) +; O3-NEXT: beqz $3, $BB7_1 +; O3-NEXT: nop +; O3-NEXT: $BB7_3: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: addiu $sp, $sp, 8 +; +; MIPS32EB-LABEL: AtomicCmpSwap32: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addiu $sp, $sp, -8 +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: sw $5, 4($sp) +; MIPS32EB-NEXT: lw $1, %got(x)($1) +; MIPS32EB-NEXT: $BB7_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $2, 0($1) +; MIPS32EB-NEXT: bne $2, $4, $BB7_3 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS32EB-NEXT: move $3, $5 +; MIPS32EB-NEXT: sc $3, 0($1) +; MIPS32EB-NEXT: beqz $3, $BB7_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: $BB7_3: # %entry +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: addiu $sp, $sp, 8 entry: %newval.addr = alloca i32, align 4 store i32 %newval, i32* %newval.addr, align 4 %tmp = load i32, i32* %newval.addr, align 4 %0 = cmpxchg i32* @x, i32 %oldval, i32 %tmp monotonic monotonic %1 = extractvalue { i32, i1 } %0, 0 ret i32 %1 -; ALL-LABEL: AtomicCmpSwap32: - -; MIPS32-ANY: lw $[[R0:[0-9]+]], %got(x) -; MIPS64-ANY: ld $[[R0:[0-9]+]], %got_disp(x)( - -; ALL: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; ALL: ll $2, 0($[[R0]]) -; NOT-MICROMIPS: bne $2, $4, [[BB1:(\$|\.L)[A-Z_0-9]+]] -; MICROMIPS: bne $2, $4, [[BB1:(\$|\.L)[A-Z_0-9]+]] -; MIPSR6: bnec $2, $4, [[BB1:(\$|\.L)[A-Z_0-9]+]] -; ALL: sc $[[R2:[0-9]+]], 0($[[R0]]) -; NOT-MICROMIPS: beqz $[[R2]], [[BB0]] -; MICROMIPS: beqzc $[[R2]], [[BB0]] -; MIPSR6: beqzc $[[R2]], [[BB0]] -; ALL: [[BB1]]: } - - @y = common global i8 0, align 1 define signext i8 @AtomicLoadAdd8(i8 signext %incr) nounwind { +; MIPS32-LABEL: AtomicLoadAdd8: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: lw $1, %got(y)($1) +; MIPS32-NEXT: addiu $2, $zero, -4 +; MIPS32-NEXT: and $2, $1, $2 +; MIPS32-NEXT: andi $1, $1, 3 +; MIPS32-NEXT: sll $3, $1, 3 +; MIPS32-NEXT: ori $1, $zero, 255 +; MIPS32-NEXT: sllv $5, $1, $3 +; MIPS32-NEXT: nor $6, $zero, $5 +; MIPS32-NEXT: sllv $4, $4, $3 +; MIPS32-NEXT: $BB8_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $7, 0($2) +; MIPS32-NEXT: addu $8, $7, $4 +; MIPS32-NEXT: and $8, $8, $5 +; MIPS32-NEXT: and $9, $7, $6 +; MIPS32-NEXT: or $9, $9, $8 +; MIPS32-NEXT: sc $9, 0($2) +; MIPS32-NEXT: beqz $9, $BB8_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: and $1, $7, $5 +; MIPS32-NEXT: srlv $1, $1, $3 +; MIPS32-NEXT: sll $1, $1, 24 +; MIPS32-NEXT: sra $1, $1, 24 +; MIPS32-NEXT: # %bb.3: # %entry +; MIPS32-NEXT: sll $1, $1, 24 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: sra $2, $1, 24 +; +; MIPS32O0-LABEL: AtomicLoadAdd8: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addiu $sp, $sp, -8 +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: lw $2, %got(y)($2) +; MIPS32O0-NEXT: addiu $25, $zero, -4 +; MIPS32O0-NEXT: and $25, $2, $25 +; MIPS32O0-NEXT: andi $2, $2, 3 +; MIPS32O0-NEXT: sll $2, $2, 3 +; MIPS32O0-NEXT: ori $1, $zero, 255 +; MIPS32O0-NEXT: sllv $1, $1, $2 +; MIPS32O0-NEXT: nor $3, $zero, $1 +; MIPS32O0-NEXT: sllv $4, $4, $2 +; MIPS32O0-NEXT: $BB8_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $6, 0($25) +; MIPS32O0-NEXT: addu $7, $6, $4 +; MIPS32O0-NEXT: and $7, $7, $1 +; MIPS32O0-NEXT: and $8, $6, $3 +; MIPS32O0-NEXT: or $8, $8, $7 +; MIPS32O0-NEXT: sc $8, 0($25) +; MIPS32O0-NEXT: beqz $8, $BB8_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: and $5, $6, $1 +; MIPS32O0-NEXT: srlv $5, $5, $2 +; MIPS32O0-NEXT: sll $5, $5, 24 +; MIPS32O0-NEXT: sra $5, $5, 24 +; MIPS32O0-NEXT: # %bb.3: # %entry +; MIPS32O0-NEXT: sw $5, 4($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: # %bb.4: # %entry +; MIPS32O0-NEXT: lw $1, 4($sp) # 4-byte Folded Reload +; MIPS32O0-NEXT: sll $2, $1, 24 +; MIPS32O0-NEXT: sra $2, $2, 24 +; MIPS32O0-NEXT: addiu $sp, $sp, 8 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicLoadAdd8: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: lw $1, %got(y)($1) +; MIPS32R2-NEXT: addiu $2, $zero, -4 +; MIPS32R2-NEXT: and $2, $1, $2 +; MIPS32R2-NEXT: andi $1, $1, 3 +; MIPS32R2-NEXT: sll $3, $1, 3 +; MIPS32R2-NEXT: ori $1, $zero, 255 +; MIPS32R2-NEXT: sllv $5, $1, $3 +; MIPS32R2-NEXT: nor $6, $zero, $5 +; MIPS32R2-NEXT: sllv $4, $4, $3 +; MIPS32R2-NEXT: $BB8_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $7, 0($2) +; MIPS32R2-NEXT: addu $8, $7, $4 +; MIPS32R2-NEXT: and $8, $8, $5 +; MIPS32R2-NEXT: and $9, $7, $6 +; MIPS32R2-NEXT: or $9, $9, $8 +; MIPS32R2-NEXT: sc $9, 0($2) +; MIPS32R2-NEXT: beqz $9, $BB8_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: and $1, $7, $5 +; MIPS32R2-NEXT: srlv $1, $1, $3 +; MIPS32R2-NEXT: seb $1, $1 +; MIPS32R2-NEXT: # %bb.3: # %entry +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: seb $2, $1 +; +; MIPS32R6-LABEL: AtomicLoadAdd8: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: lw $1, %got(y)($1) +; MIPS32R6-NEXT: addiu $2, $zero, -4 +; MIPS32R6-NEXT: and $2, $1, $2 +; MIPS32R6-NEXT: andi $1, $1, 3 +; MIPS32R6-NEXT: sll $3, $1, 3 +; MIPS32R6-NEXT: ori $1, $zero, 255 +; MIPS32R6-NEXT: sllv $5, $1, $3 +; MIPS32R6-NEXT: nor $6, $zero, $5 +; MIPS32R6-NEXT: sllv $4, $4, $3 +; MIPS32R6-NEXT: $BB8_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $7, 0($2) +; MIPS32R6-NEXT: addu $8, $7, $4 +; MIPS32R6-NEXT: and $8, $8, $5 +; MIPS32R6-NEXT: and $9, $7, $6 +; MIPS32R6-NEXT: or $9, $9, $8 +; MIPS32R6-NEXT: sc $9, 0($2) +; MIPS32R6-NEXT: beqzc $9, $BB8_1 +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: and $1, $7, $5 +; MIPS32R6-NEXT: srlv $1, $1, $3 +; MIPS32R6-NEXT: seb $1, $1 +; MIPS32R6-NEXT: # %bb.3: # %entry +; MIPS32R6-NEXT: jr $ra +; MIPS32R6-NEXT: seb $2, $1 +; +; MIPS32R6O0-LABEL: AtomicLoadAdd8: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -8 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $4 +; MIPS32R6O0-NEXT: lw $2, %got(y)($2) +; MIPS32R6O0-NEXT: addiu $1, $zero, -4 +; MIPS32R6O0-NEXT: and $1, $2, $1 +; MIPS32R6O0-NEXT: andi $2, $2, 3 +; MIPS32R6O0-NEXT: sll $2, $2, 3 +; MIPS32R6O0-NEXT: ori $3, $zero, 255 +; MIPS32R6O0-NEXT: sllv $3, $3, $2 +; MIPS32R6O0-NEXT: nor $5, $zero, $3 +; MIPS32R6O0-NEXT: sllv $4, $4, $2 +; MIPS32R6O0-NEXT: $BB8_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $7, 0($1) +; MIPS32R6O0-NEXT: addu $8, $7, $4 +; MIPS32R6O0-NEXT: and $8, $8, $3 +; MIPS32R6O0-NEXT: and $9, $7, $5 +; MIPS32R6O0-NEXT: or $9, $9, $8 +; MIPS32R6O0-NEXT: sc $9, 0($1) +; MIPS32R6O0-NEXT: beqzc $9, $BB8_1 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: and $6, $7, $3 +; MIPS32R6O0-NEXT: srlv $6, $6, $2 +; MIPS32R6O0-NEXT: seb $6, $6 +; MIPS32R6O0-NEXT: # %bb.3: # %entry +; MIPS32R6O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $6, 0($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: # %bb.4: # %entry +; MIPS32R6O0-NEXT: lw $1, 0($sp) # 4-byte Folded Reload +; MIPS32R6O0-NEXT: seb $2, $1 +; MIPS32R6O0-NEXT: addiu $sp, $sp, 8 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicLoadAdd8: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd8))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd8))) +; MIPS4-NEXT: ld $1, %got_disp(y)($1) +; MIPS4-NEXT: daddiu $2, $zero, -4 +; MIPS4-NEXT: and $2, $1, $2 +; MIPS4-NEXT: andi $1, $1, 3 +; MIPS4-NEXT: sll $3, $1, 3 +; MIPS4-NEXT: ori $1, $zero, 255 +; MIPS4-NEXT: sllv $5, $1, $3 +; MIPS4-NEXT: nor $6, $zero, $5 +; MIPS4-NEXT: sllv $4, $4, $3 +; MIPS4-NEXT: .LBB8_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $7, 0($2) +; MIPS4-NEXT: addu $8, $7, $4 +; MIPS4-NEXT: and $8, $8, $5 +; MIPS4-NEXT: and $9, $7, $6 +; MIPS4-NEXT: or $9, $9, $8 +; MIPS4-NEXT: sc $9, 0($2) +; MIPS4-NEXT: beqz $9, .LBB8_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: and $1, $7, $5 +; MIPS4-NEXT: srlv $1, $1, $3 +; MIPS4-NEXT: sll $1, $1, 24 +; MIPS4-NEXT: sra $1, $1, 24 +; MIPS4-NEXT: # %bb.3: # %entry +; MIPS4-NEXT: sll $1, $1, 24 +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: sra $2, $1, 24 +; +; MIPS64-LABEL: AtomicLoadAdd8: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd8))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd8))) +; MIPS64-NEXT: ld $1, %got_disp(y)($1) +; MIPS64-NEXT: daddiu $2, $zero, -4 +; MIPS64-NEXT: and $2, $1, $2 +; MIPS64-NEXT: andi $1, $1, 3 +; MIPS64-NEXT: sll $3, $1, 3 +; MIPS64-NEXT: ori $1, $zero, 255 +; MIPS64-NEXT: sllv $5, $1, $3 +; MIPS64-NEXT: nor $6, $zero, $5 +; MIPS64-NEXT: sllv $4, $4, $3 +; MIPS64-NEXT: .LBB8_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $7, 0($2) +; MIPS64-NEXT: addu $8, $7, $4 +; MIPS64-NEXT: and $8, $8, $5 +; MIPS64-NEXT: and $9, $7, $6 +; MIPS64-NEXT: or $9, $9, $8 +; MIPS64-NEXT: sc $9, 0($2) +; MIPS64-NEXT: beqz $9, .LBB8_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: and $1, $7, $5 +; MIPS64-NEXT: srlv $1, $1, $3 +; MIPS64-NEXT: sll $1, $1, 24 +; MIPS64-NEXT: sra $1, $1, 24 +; MIPS64-NEXT: # %bb.3: # %entry +; MIPS64-NEXT: sll $1, $1, 24 +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: sra $2, $1, 24 +; +; MIPS64R2-LABEL: AtomicLoadAdd8: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd8))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd8))) +; MIPS64R2-NEXT: ld $1, %got_disp(y)($1) +; MIPS64R2-NEXT: daddiu $2, $zero, -4 +; MIPS64R2-NEXT: and $2, $1, $2 +; MIPS64R2-NEXT: andi $1, $1, 3 +; MIPS64R2-NEXT: sll $3, $1, 3 +; MIPS64R2-NEXT: ori $1, $zero, 255 +; MIPS64R2-NEXT: sllv $5, $1, $3 +; MIPS64R2-NEXT: nor $6, $zero, $5 +; MIPS64R2-NEXT: sllv $4, $4, $3 +; MIPS64R2-NEXT: .LBB8_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $7, 0($2) +; MIPS64R2-NEXT: addu $8, $7, $4 +; MIPS64R2-NEXT: and $8, $8, $5 +; MIPS64R2-NEXT: and $9, $7, $6 +; MIPS64R2-NEXT: or $9, $9, $8 +; MIPS64R2-NEXT: sc $9, 0($2) +; MIPS64R2-NEXT: beqz $9, .LBB8_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: and $1, $7, $5 +; MIPS64R2-NEXT: srlv $1, $1, $3 +; MIPS64R2-NEXT: seb $1, $1 +; MIPS64R2-NEXT: # %bb.3: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: seb $2, $1 +; +; MIPS64R6-LABEL: AtomicLoadAdd8: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd8))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd8))) +; MIPS64R6-NEXT: ld $1, %got_disp(y)($1) +; MIPS64R6-NEXT: daddiu $2, $zero, -4 +; MIPS64R6-NEXT: and $2, $1, $2 +; MIPS64R6-NEXT: andi $1, $1, 3 +; MIPS64R6-NEXT: sll $3, $1, 3 +; MIPS64R6-NEXT: ori $1, $zero, 255 +; MIPS64R6-NEXT: sllv $5, $1, $3 +; MIPS64R6-NEXT: nor $6, $zero, $5 +; MIPS64R6-NEXT: sllv $4, $4, $3 +; MIPS64R6-NEXT: .LBB8_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $7, 0($2) +; MIPS64R6-NEXT: addu $8, $7, $4 +; MIPS64R6-NEXT: and $8, $8, $5 +; MIPS64R6-NEXT: and $9, $7, $6 +; MIPS64R6-NEXT: or $9, $9, $8 +; MIPS64R6-NEXT: sc $9, 0($2) +; MIPS64R6-NEXT: beqzc $9, .LBB8_1 +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: and $1, $7, $5 +; MIPS64R6-NEXT: srlv $1, $1, $3 +; MIPS64R6-NEXT: seb $1, $1 +; MIPS64R6-NEXT: # %bb.3: # %entry +; MIPS64R6-NEXT: jr $ra +; MIPS64R6-NEXT: seb $2, $1 +; +; MIPS64R6O0-LABEL: AtomicLoadAdd8: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd8))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd8))) +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(y)($1) +; MIPS64R6O0-NEXT: daddiu $4, $zero, -4 +; MIPS64R6O0-NEXT: and $4, $1, $4 +; MIPS64R6O0-NEXT: andi $3, $1, 3 +; MIPS64R6O0-NEXT: xori $3, $3, 3 +; MIPS64R6O0-NEXT: sll $3, $3, 3 +; MIPS64R6O0-NEXT: ori $5, $zero, 255 +; MIPS64R6O0-NEXT: sllv $5, $5, $3 +; MIPS64R6O0-NEXT: nor $6, $zero, $5 +; MIPS64R6O0-NEXT: sllv $2, $2, $3 +; MIPS64R6O0-NEXT: .LBB8_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $8, 0($4) +; MIPS64R6O0-NEXT: addu $9, $8, $2 +; MIPS64R6O0-NEXT: and $9, $9, $5 +; MIPS64R6O0-NEXT: and $10, $8, $6 +; MIPS64R6O0-NEXT: or $10, $10, $9 +; MIPS64R6O0-NEXT: sc $10, 0($4) +; MIPS64R6O0-NEXT: beqzc $10, .LBB8_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: and $7, $8, $5 +; MIPS64R6O0-NEXT: srlv $7, $7, $3 +; MIPS64R6O0-NEXT: seb $7, $7 +; MIPS64R6O0-NEXT: # %bb.3: # %entry +; MIPS64R6O0-NEXT: sw $7, 12($sp) # 4-byte Folded Spill +; MIPS64R6O0-NEXT: # %bb.4: # %entry +; MIPS64R6O0-NEXT: lw $1, 12($sp) # 4-byte Folded Reload +; MIPS64R6O0-NEXT: seb $2, $1 +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicLoadAdd8: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: lw $1, %got(y)($2) +; MM32-NEXT: addiu $2, $zero, -4 +; MM32-NEXT: and $2, $1, $2 +; MM32-NEXT: andi $1, $1, 3 +; MM32-NEXT: sll $3, $1, 3 +; MM32-NEXT: ori $1, $zero, 255 +; MM32-NEXT: sllv $5, $1, $3 +; MM32-NEXT: nor $6, $zero, $5 +; MM32-NEXT: sllv $4, $4, $3 +; MM32-NEXT: $BB8_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $7, 0($2) +; MM32-NEXT: addu $8, $7, $4 +; MM32-NEXT: and $8, $8, $5 +; MM32-NEXT: and $9, $7, $6 +; MM32-NEXT: or $9, $9, $8 +; MM32-NEXT: sc $9, 0($2) +; MM32-NEXT: beqzc $9, $BB8_1 +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: and $1, $7, $5 +; MM32-NEXT: srlv $1, $1, $3 +; MM32-NEXT: seb $1, $1 +; MM32-NEXT: # %bb.3: # %entry +; MM32-NEXT: jr $ra +; MM32-NEXT: seb $2, $1 +; +; O1-LABEL: AtomicLoadAdd8: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: lw $1, %got(y)($1) +; O1-NEXT: addiu $2, $zero, -4 +; O1-NEXT: and $2, $1, $2 +; O1-NEXT: andi $1, $1, 3 +; O1-NEXT: sll $3, $1, 3 +; O1-NEXT: ori $1, $zero, 255 +; O1-NEXT: sllv $5, $1, $3 +; O1-NEXT: nor $6, $zero, $5 +; O1-NEXT: sllv $4, $4, $3 +; O1-NEXT: $BB8_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $7, 0($2) +; O1-NEXT: addu $8, $7, $4 +; O1-NEXT: and $8, $8, $5 +; O1-NEXT: and $9, $7, $6 +; O1-NEXT: or $9, $9, $8 +; O1-NEXT: sc $9, 0($2) +; O1-NEXT: beqz $9, $BB8_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: and $1, $7, $5 +; O1-NEXT: srlv $1, $1, $3 +; O1-NEXT: sll $1, $1, 24 +; O1-NEXT: sra $1, $1, 24 +; O1-NEXT: # %bb.3: # %entry +; O1-NEXT: sll $1, $1, 24 +; O1-NEXT: jr $ra +; O1-NEXT: sra $2, $1, 24 +; +; O2-LABEL: AtomicLoadAdd8: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: lw $1, %got(y)($1) +; O2-NEXT: addiu $2, $zero, -4 +; O2-NEXT: and $2, $1, $2 +; O2-NEXT: andi $1, $1, 3 +; O2-NEXT: sll $3, $1, 3 +; O2-NEXT: ori $1, $zero, 255 +; O2-NEXT: sllv $5, $1, $3 +; O2-NEXT: nor $6, $zero, $5 +; O2-NEXT: sllv $4, $4, $3 +; O2-NEXT: $BB8_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $7, 0($2) +; O2-NEXT: addu $8, $7, $4 +; O2-NEXT: and $8, $8, $5 +; O2-NEXT: and $9, $7, $6 +; O2-NEXT: or $9, $9, $8 +; O2-NEXT: sc $9, 0($2) +; O2-NEXT: beqz $9, $BB8_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: and $1, $7, $5 +; O2-NEXT: srlv $1, $1, $3 +; O2-NEXT: sll $1, $1, 24 +; O2-NEXT: sra $1, $1, 24 +; O2-NEXT: # %bb.3: # %entry +; O2-NEXT: sll $1, $1, 24 +; O2-NEXT: jr $ra +; O2-NEXT: sra $2, $1, 24 +; +; O3-LABEL: AtomicLoadAdd8: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: addiu $2, $zero, -4 +; O3-NEXT: lw $1, %got(y)($1) +; O3-NEXT: and $2, $1, $2 +; O3-NEXT: andi $1, $1, 3 +; O3-NEXT: sll $3, $1, 3 +; O3-NEXT: ori $1, $zero, 255 +; O3-NEXT: sllv $5, $1, $3 +; O3-NEXT: sllv $4, $4, $3 +; O3-NEXT: nor $6, $zero, $5 +; O3-NEXT: $BB8_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $7, 0($2) +; O3-NEXT: addu $8, $7, $4 +; O3-NEXT: and $8, $8, $5 +; O3-NEXT: and $9, $7, $6 +; O3-NEXT: or $9, $9, $8 +; O3-NEXT: sc $9, 0($2) +; O3-NEXT: beqz $9, $BB8_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: and $1, $7, $5 +; O3-NEXT: srlv $1, $1, $3 +; O3-NEXT: sll $1, $1, 24 +; O3-NEXT: sra $1, $1, 24 +; O3-NEXT: # %bb.3: # %entry +; O3-NEXT: sll $1, $1, 24 +; O3-NEXT: jr $ra +; O3-NEXT: sra $2, $1, 24 +; +; MIPS32EB-LABEL: AtomicLoadAdd8: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: lw $1, %got(y)($1) +; MIPS32EB-NEXT: addiu $2, $zero, -4 +; MIPS32EB-NEXT: and $2, $1, $2 +; MIPS32EB-NEXT: andi $1, $1, 3 +; MIPS32EB-NEXT: xori $1, $1, 3 +; MIPS32EB-NEXT: sll $3, $1, 3 +; MIPS32EB-NEXT: ori $1, $zero, 255 +; MIPS32EB-NEXT: sllv $5, $1, $3 +; MIPS32EB-NEXT: nor $6, $zero, $5 +; MIPS32EB-NEXT: sllv $4, $4, $3 +; MIPS32EB-NEXT: $BB8_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $7, 0($2) +; MIPS32EB-NEXT: addu $8, $7, $4 +; MIPS32EB-NEXT: and $8, $8, $5 +; MIPS32EB-NEXT: and $9, $7, $6 +; MIPS32EB-NEXT: or $9, $9, $8 +; MIPS32EB-NEXT: sc $9, 0($2) +; MIPS32EB-NEXT: beqz $9, $BB8_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: and $1, $7, $5 +; MIPS32EB-NEXT: srlv $1, $1, $3 +; MIPS32EB-NEXT: sll $1, $1, 24 +; MIPS32EB-NEXT: sra $1, $1, 24 +; MIPS32EB-NEXT: # %bb.3: # %entry +; MIPS32EB-NEXT: sll $1, $1, 24 +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: sra $2, $1, 24 entry: %0 = atomicrmw add i8* @y, i8 %incr monotonic ret i8 %0 - -; ALL-LABEL: AtomicLoadAdd8: - -; MIPS32-ANY: lw $[[R0:[0-9]+]], %got(y) -; MIPS64-ANY: ld $[[R0:[0-9]+]], %got_disp(y)( - -; ALL: addiu $[[R1:[0-9]+]], $zero, -4 -; ALL: and $[[R2:[0-9]+]], $[[R0]], $[[R1]] -; ALL: andi $[[R3:[0-9]+]], $[[R0]], 3 -; CHECK-EB: xori $[[R4:[0-9]+]], $[[R3]], 3 -; CHECK-EB: sll $[[R5:[0-9]+]], $[[R4]], 3 -; CHECK-EL: sll $[[R5:[0-9]+]], $[[R3]], 3 -; ALL: ori $[[R6:[0-9]+]], $zero, 255 -; ALL: sllv $[[R7:[0-9]+]], $[[R6]], $[[R5]] -; ALL: nor $[[R8:[0-9]+]], $zero, $[[R7]] -; ALL: sllv $[[R9:[0-9]+]], $4, $[[R5]] - -; O0: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; O0: ld $[[R10:[0-9]+]] -; O0-NEXT: ll $[[R11:[0-9]+]], 0($[[R10]]) - -; ALL: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; ALL: ll $[[R12:[0-9]+]], 0($[[R2]]) -; ALL: addu $[[R13:[0-9]+]], $[[R12]], $[[R9]] -; ALL: and $[[R14:[0-9]+]], $[[R13]], $[[R7]] -; ALL: and $[[R15:[0-9]+]], $[[R12]], $[[R8]] -; ALL: or $[[R16:[0-9]+]], $[[R15]], $[[R14]] -; ALL: sc $[[R16]], 0($[[R2]]) -; NOT-MICROMIPS: beqz $[[R16]], [[BB0]] -; MICROMIPS: beqzc $[[R16]], [[BB0]] -; MIPSR6: beqzc $[[R16]], [[BB0]] - -; ALL: and $[[R17:[0-9]+]], $[[R12]], $[[R7]] -; ALL: srlv $[[R18:[0-9]+]], $[[R17]], $[[R5]] - -; NO-SEB-SEH: sll $[[R19:[0-9]+]], $[[R18]], 24 -; NO-SEB-SEH: sra $2, $[[R19]], 24 - -; HAS-SEB-SEH: seb $2, $[[R18]] } define signext i8 @AtomicLoadSub8(i8 signext %incr) nounwind { +; MIPS32-LABEL: AtomicLoadSub8: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: lw $1, %got(y)($1) +; MIPS32-NEXT: addiu $2, $zero, -4 +; MIPS32-NEXT: and $2, $1, $2 +; MIPS32-NEXT: andi $1, $1, 3 +; MIPS32-NEXT: sll $3, $1, 3 +; MIPS32-NEXT: ori $1, $zero, 255 +; MIPS32-NEXT: sllv $5, $1, $3 +; MIPS32-NEXT: nor $6, $zero, $5 +; MIPS32-NEXT: sllv $4, $4, $3 +; MIPS32-NEXT: $BB9_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $7, 0($2) +; MIPS32-NEXT: subu $8, $7, $4 +; MIPS32-NEXT: and $8, $8, $5 +; MIPS32-NEXT: and $9, $7, $6 +; MIPS32-NEXT: or $9, $9, $8 +; MIPS32-NEXT: sc $9, 0($2) +; MIPS32-NEXT: beqz $9, $BB9_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: and $1, $7, $5 +; MIPS32-NEXT: srlv $1, $1, $3 +; MIPS32-NEXT: sll $1, $1, 24 +; MIPS32-NEXT: sra $1, $1, 24 +; MIPS32-NEXT: # %bb.3: # %entry +; MIPS32-NEXT: sll $1, $1, 24 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: sra $2, $1, 24 +; +; MIPS32O0-LABEL: AtomicLoadSub8: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addiu $sp, $sp, -8 +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: lw $2, %got(y)($2) +; MIPS32O0-NEXT: addiu $25, $zero, -4 +; MIPS32O0-NEXT: and $25, $2, $25 +; MIPS32O0-NEXT: andi $2, $2, 3 +; MIPS32O0-NEXT: sll $2, $2, 3 +; MIPS32O0-NEXT: ori $1, $zero, 255 +; MIPS32O0-NEXT: sllv $1, $1, $2 +; MIPS32O0-NEXT: nor $3, $zero, $1 +; MIPS32O0-NEXT: sllv $4, $4, $2 +; MIPS32O0-NEXT: $BB9_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $6, 0($25) +; MIPS32O0-NEXT: subu $7, $6, $4 +; MIPS32O0-NEXT: and $7, $7, $1 +; MIPS32O0-NEXT: and $8, $6, $3 +; MIPS32O0-NEXT: or $8, $8, $7 +; MIPS32O0-NEXT: sc $8, 0($25) +; MIPS32O0-NEXT: beqz $8, $BB9_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: and $5, $6, $1 +; MIPS32O0-NEXT: srlv $5, $5, $2 +; MIPS32O0-NEXT: sll $5, $5, 24 +; MIPS32O0-NEXT: sra $5, $5, 24 +; MIPS32O0-NEXT: # %bb.3: # %entry +; MIPS32O0-NEXT: sw $5, 4($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: # %bb.4: # %entry +; MIPS32O0-NEXT: lw $1, 4($sp) # 4-byte Folded Reload +; MIPS32O0-NEXT: sll $2, $1, 24 +; MIPS32O0-NEXT: sra $2, $2, 24 +; MIPS32O0-NEXT: addiu $sp, $sp, 8 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicLoadSub8: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: lw $1, %got(y)($1) +; MIPS32R2-NEXT: addiu $2, $zero, -4 +; MIPS32R2-NEXT: and $2, $1, $2 +; MIPS32R2-NEXT: andi $1, $1, 3 +; MIPS32R2-NEXT: sll $3, $1, 3 +; MIPS32R2-NEXT: ori $1, $zero, 255 +; MIPS32R2-NEXT: sllv $5, $1, $3 +; MIPS32R2-NEXT: nor $6, $zero, $5 +; MIPS32R2-NEXT: sllv $4, $4, $3 +; MIPS32R2-NEXT: $BB9_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $7, 0($2) +; MIPS32R2-NEXT: subu $8, $7, $4 +; MIPS32R2-NEXT: and $8, $8, $5 +; MIPS32R2-NEXT: and $9, $7, $6 +; MIPS32R2-NEXT: or $9, $9, $8 +; MIPS32R2-NEXT: sc $9, 0($2) +; MIPS32R2-NEXT: beqz $9, $BB9_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: and $1, $7, $5 +; MIPS32R2-NEXT: srlv $1, $1, $3 +; MIPS32R2-NEXT: seb $1, $1 +; MIPS32R2-NEXT: # %bb.3: # %entry +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: seb $2, $1 +; +; MIPS32R6-LABEL: AtomicLoadSub8: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: lw $1, %got(y)($1) +; MIPS32R6-NEXT: addiu $2, $zero, -4 +; MIPS32R6-NEXT: and $2, $1, $2 +; MIPS32R6-NEXT: andi $1, $1, 3 +; MIPS32R6-NEXT: sll $3, $1, 3 +; MIPS32R6-NEXT: ori $1, $zero, 255 +; MIPS32R6-NEXT: sllv $5, $1, $3 +; MIPS32R6-NEXT: nor $6, $zero, $5 +; MIPS32R6-NEXT: sllv $4, $4, $3 +; MIPS32R6-NEXT: $BB9_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $7, 0($2) +; MIPS32R6-NEXT: subu $8, $7, $4 +; MIPS32R6-NEXT: and $8, $8, $5 +; MIPS32R6-NEXT: and $9, $7, $6 +; MIPS32R6-NEXT: or $9, $9, $8 +; MIPS32R6-NEXT: sc $9, 0($2) +; MIPS32R6-NEXT: beqzc $9, $BB9_1 +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: and $1, $7, $5 +; MIPS32R6-NEXT: srlv $1, $1, $3 +; MIPS32R6-NEXT: seb $1, $1 +; MIPS32R6-NEXT: # %bb.3: # %entry +; MIPS32R6-NEXT: jr $ra +; MIPS32R6-NEXT: seb $2, $1 +; +; MIPS32R6O0-LABEL: AtomicLoadSub8: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -8 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $4 +; MIPS32R6O0-NEXT: lw $2, %got(y)($2) +; MIPS32R6O0-NEXT: addiu $1, $zero, -4 +; MIPS32R6O0-NEXT: and $1, $2, $1 +; MIPS32R6O0-NEXT: andi $2, $2, 3 +; MIPS32R6O0-NEXT: sll $2, $2, 3 +; MIPS32R6O0-NEXT: ori $3, $zero, 255 +; MIPS32R6O0-NEXT: sllv $3, $3, $2 +; MIPS32R6O0-NEXT: nor $5, $zero, $3 +; MIPS32R6O0-NEXT: sllv $4, $4, $2 +; MIPS32R6O0-NEXT: $BB9_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $7, 0($1) +; MIPS32R6O0-NEXT: subu $8, $7, $4 +; MIPS32R6O0-NEXT: and $8, $8, $3 +; MIPS32R6O0-NEXT: and $9, $7, $5 +; MIPS32R6O0-NEXT: or $9, $9, $8 +; MIPS32R6O0-NEXT: sc $9, 0($1) +; MIPS32R6O0-NEXT: beqzc $9, $BB9_1 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: and $6, $7, $3 +; MIPS32R6O0-NEXT: srlv $6, $6, $2 +; MIPS32R6O0-NEXT: seb $6, $6 +; MIPS32R6O0-NEXT: # %bb.3: # %entry +; MIPS32R6O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $6, 0($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: # %bb.4: # %entry +; MIPS32R6O0-NEXT: lw $1, 0($sp) # 4-byte Folded Reload +; MIPS32R6O0-NEXT: seb $2, $1 +; MIPS32R6O0-NEXT: addiu $sp, $sp, 8 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicLoadSub8: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub8))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub8))) +; MIPS4-NEXT: ld $1, %got_disp(y)($1) +; MIPS4-NEXT: daddiu $2, $zero, -4 +; MIPS4-NEXT: and $2, $1, $2 +; MIPS4-NEXT: andi $1, $1, 3 +; MIPS4-NEXT: sll $3, $1, 3 +; MIPS4-NEXT: ori $1, $zero, 255 +; MIPS4-NEXT: sllv $5, $1, $3 +; MIPS4-NEXT: nor $6, $zero, $5 +; MIPS4-NEXT: sllv $4, $4, $3 +; MIPS4-NEXT: .LBB9_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $7, 0($2) +; MIPS4-NEXT: subu $8, $7, $4 +; MIPS4-NEXT: and $8, $8, $5 +; MIPS4-NEXT: and $9, $7, $6 +; MIPS4-NEXT: or $9, $9, $8 +; MIPS4-NEXT: sc $9, 0($2) +; MIPS4-NEXT: beqz $9, .LBB9_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: and $1, $7, $5 +; MIPS4-NEXT: srlv $1, $1, $3 +; MIPS4-NEXT: sll $1, $1, 24 +; MIPS4-NEXT: sra $1, $1, 24 +; MIPS4-NEXT: # %bb.3: # %entry +; MIPS4-NEXT: sll $1, $1, 24 +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: sra $2, $1, 24 +; +; MIPS64-LABEL: AtomicLoadSub8: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub8))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub8))) +; MIPS64-NEXT: ld $1, %got_disp(y)($1) +; MIPS64-NEXT: daddiu $2, $zero, -4 +; MIPS64-NEXT: and $2, $1, $2 +; MIPS64-NEXT: andi $1, $1, 3 +; MIPS64-NEXT: sll $3, $1, 3 +; MIPS64-NEXT: ori $1, $zero, 255 +; MIPS64-NEXT: sllv $5, $1, $3 +; MIPS64-NEXT: nor $6, $zero, $5 +; MIPS64-NEXT: sllv $4, $4, $3 +; MIPS64-NEXT: .LBB9_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $7, 0($2) +; MIPS64-NEXT: subu $8, $7, $4 +; MIPS64-NEXT: and $8, $8, $5 +; MIPS64-NEXT: and $9, $7, $6 +; MIPS64-NEXT: or $9, $9, $8 +; MIPS64-NEXT: sc $9, 0($2) +; MIPS64-NEXT: beqz $9, .LBB9_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: and $1, $7, $5 +; MIPS64-NEXT: srlv $1, $1, $3 +; MIPS64-NEXT: sll $1, $1, 24 +; MIPS64-NEXT: sra $1, $1, 24 +; MIPS64-NEXT: # %bb.3: # %entry +; MIPS64-NEXT: sll $1, $1, 24 +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: sra $2, $1, 24 +; +; MIPS64R2-LABEL: AtomicLoadSub8: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub8))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub8))) +; MIPS64R2-NEXT: ld $1, %got_disp(y)($1) +; MIPS64R2-NEXT: daddiu $2, $zero, -4 +; MIPS64R2-NEXT: and $2, $1, $2 +; MIPS64R2-NEXT: andi $1, $1, 3 +; MIPS64R2-NEXT: sll $3, $1, 3 +; MIPS64R2-NEXT: ori $1, $zero, 255 +; MIPS64R2-NEXT: sllv $5, $1, $3 +; MIPS64R2-NEXT: nor $6, $zero, $5 +; MIPS64R2-NEXT: sllv $4, $4, $3 +; MIPS64R2-NEXT: .LBB9_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $7, 0($2) +; MIPS64R2-NEXT: subu $8, $7, $4 +; MIPS64R2-NEXT: and $8, $8, $5 +; MIPS64R2-NEXT: and $9, $7, $6 +; MIPS64R2-NEXT: or $9, $9, $8 +; MIPS64R2-NEXT: sc $9, 0($2) +; MIPS64R2-NEXT: beqz $9, .LBB9_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: and $1, $7, $5 +; MIPS64R2-NEXT: srlv $1, $1, $3 +; MIPS64R2-NEXT: seb $1, $1 +; MIPS64R2-NEXT: # %bb.3: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: seb $2, $1 +; +; MIPS64R6-LABEL: AtomicLoadSub8: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub8))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub8))) +; MIPS64R6-NEXT: ld $1, %got_disp(y)($1) +; MIPS64R6-NEXT: daddiu $2, $zero, -4 +; MIPS64R6-NEXT: and $2, $1, $2 +; MIPS64R6-NEXT: andi $1, $1, 3 +; MIPS64R6-NEXT: sll $3, $1, 3 +; MIPS64R6-NEXT: ori $1, $zero, 255 +; MIPS64R6-NEXT: sllv $5, $1, $3 +; MIPS64R6-NEXT: nor $6, $zero, $5 +; MIPS64R6-NEXT: sllv $4, $4, $3 +; MIPS64R6-NEXT: .LBB9_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $7, 0($2) +; MIPS64R6-NEXT: subu $8, $7, $4 +; MIPS64R6-NEXT: and $8, $8, $5 +; MIPS64R6-NEXT: and $9, $7, $6 +; MIPS64R6-NEXT: or $9, $9, $8 +; MIPS64R6-NEXT: sc $9, 0($2) +; MIPS64R6-NEXT: beqzc $9, .LBB9_1 +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: and $1, $7, $5 +; MIPS64R6-NEXT: srlv $1, $1, $3 +; MIPS64R6-NEXT: seb $1, $1 +; MIPS64R6-NEXT: # %bb.3: # %entry +; MIPS64R6-NEXT: jr $ra +; MIPS64R6-NEXT: seb $2, $1 +; +; MIPS64R6O0-LABEL: AtomicLoadSub8: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub8))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub8))) +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(y)($1) +; MIPS64R6O0-NEXT: daddiu $4, $zero, -4 +; MIPS64R6O0-NEXT: and $4, $1, $4 +; MIPS64R6O0-NEXT: andi $3, $1, 3 +; MIPS64R6O0-NEXT: xori $3, $3, 3 +; MIPS64R6O0-NEXT: sll $3, $3, 3 +; MIPS64R6O0-NEXT: ori $5, $zero, 255 +; MIPS64R6O0-NEXT: sllv $5, $5, $3 +; MIPS64R6O0-NEXT: nor $6, $zero, $5 +; MIPS64R6O0-NEXT: sllv $2, $2, $3 +; MIPS64R6O0-NEXT: .LBB9_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $8, 0($4) +; MIPS64R6O0-NEXT: subu $9, $8, $2 +; MIPS64R6O0-NEXT: and $9, $9, $5 +; MIPS64R6O0-NEXT: and $10, $8, $6 +; MIPS64R6O0-NEXT: or $10, $10, $9 +; MIPS64R6O0-NEXT: sc $10, 0($4) +; MIPS64R6O0-NEXT: beqzc $10, .LBB9_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: and $7, $8, $5 +; MIPS64R6O0-NEXT: srlv $7, $7, $3 +; MIPS64R6O0-NEXT: seb $7, $7 +; MIPS64R6O0-NEXT: # %bb.3: # %entry +; MIPS64R6O0-NEXT: sw $7, 12($sp) # 4-byte Folded Spill +; MIPS64R6O0-NEXT: # %bb.4: # %entry +; MIPS64R6O0-NEXT: lw $1, 12($sp) # 4-byte Folded Reload +; MIPS64R6O0-NEXT: seb $2, $1 +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicLoadSub8: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: lw $1, %got(y)($2) +; MM32-NEXT: addiu $2, $zero, -4 +; MM32-NEXT: and $2, $1, $2 +; MM32-NEXT: andi $1, $1, 3 +; MM32-NEXT: sll $3, $1, 3 +; MM32-NEXT: ori $1, $zero, 255 +; MM32-NEXT: sllv $5, $1, $3 +; MM32-NEXT: nor $6, $zero, $5 +; MM32-NEXT: sllv $4, $4, $3 +; MM32-NEXT: $BB9_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $7, 0($2) +; MM32-NEXT: subu $8, $7, $4 +; MM32-NEXT: and $8, $8, $5 +; MM32-NEXT: and $9, $7, $6 +; MM32-NEXT: or $9, $9, $8 +; MM32-NEXT: sc $9, 0($2) +; MM32-NEXT: beqzc $9, $BB9_1 +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: and $1, $7, $5 +; MM32-NEXT: srlv $1, $1, $3 +; MM32-NEXT: seb $1, $1 +; MM32-NEXT: # %bb.3: # %entry +; MM32-NEXT: jr $ra +; MM32-NEXT: seb $2, $1 +; +; O1-LABEL: AtomicLoadSub8: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: lw $1, %got(y)($1) +; O1-NEXT: addiu $2, $zero, -4 +; O1-NEXT: and $2, $1, $2 +; O1-NEXT: andi $1, $1, 3 +; O1-NEXT: sll $3, $1, 3 +; O1-NEXT: ori $1, $zero, 255 +; O1-NEXT: sllv $5, $1, $3 +; O1-NEXT: nor $6, $zero, $5 +; O1-NEXT: sllv $4, $4, $3 +; O1-NEXT: $BB9_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $7, 0($2) +; O1-NEXT: subu $8, $7, $4 +; O1-NEXT: and $8, $8, $5 +; O1-NEXT: and $9, $7, $6 +; O1-NEXT: or $9, $9, $8 +; O1-NEXT: sc $9, 0($2) +; O1-NEXT: beqz $9, $BB9_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: and $1, $7, $5 +; O1-NEXT: srlv $1, $1, $3 +; O1-NEXT: sll $1, $1, 24 +; O1-NEXT: sra $1, $1, 24 +; O1-NEXT: # %bb.3: # %entry +; O1-NEXT: sll $1, $1, 24 +; O1-NEXT: jr $ra +; O1-NEXT: sra $2, $1, 24 +; +; O2-LABEL: AtomicLoadSub8: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: lw $1, %got(y)($1) +; O2-NEXT: addiu $2, $zero, -4 +; O2-NEXT: and $2, $1, $2 +; O2-NEXT: andi $1, $1, 3 +; O2-NEXT: sll $3, $1, 3 +; O2-NEXT: ori $1, $zero, 255 +; O2-NEXT: sllv $5, $1, $3 +; O2-NEXT: nor $6, $zero, $5 +; O2-NEXT: sllv $4, $4, $3 +; O2-NEXT: $BB9_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $7, 0($2) +; O2-NEXT: subu $8, $7, $4 +; O2-NEXT: and $8, $8, $5 +; O2-NEXT: and $9, $7, $6 +; O2-NEXT: or $9, $9, $8 +; O2-NEXT: sc $9, 0($2) +; O2-NEXT: beqz $9, $BB9_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: and $1, $7, $5 +; O2-NEXT: srlv $1, $1, $3 +; O2-NEXT: sll $1, $1, 24 +; O2-NEXT: sra $1, $1, 24 +; O2-NEXT: # %bb.3: # %entry +; O2-NEXT: sll $1, $1, 24 +; O2-NEXT: jr $ra +; O2-NEXT: sra $2, $1, 24 +; +; O3-LABEL: AtomicLoadSub8: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: addiu $2, $zero, -4 +; O3-NEXT: lw $1, %got(y)($1) +; O3-NEXT: and $2, $1, $2 +; O3-NEXT: andi $1, $1, 3 +; O3-NEXT: sll $3, $1, 3 +; O3-NEXT: ori $1, $zero, 255 +; O3-NEXT: sllv $5, $1, $3 +; O3-NEXT: sllv $4, $4, $3 +; O3-NEXT: nor $6, $zero, $5 +; O3-NEXT: $BB9_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $7, 0($2) +; O3-NEXT: subu $8, $7, $4 +; O3-NEXT: and $8, $8, $5 +; O3-NEXT: and $9, $7, $6 +; O3-NEXT: or $9, $9, $8 +; O3-NEXT: sc $9, 0($2) +; O3-NEXT: beqz $9, $BB9_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: and $1, $7, $5 +; O3-NEXT: srlv $1, $1, $3 +; O3-NEXT: sll $1, $1, 24 +; O3-NEXT: sra $1, $1, 24 +; O3-NEXT: # %bb.3: # %entry +; O3-NEXT: sll $1, $1, 24 +; O3-NEXT: jr $ra +; O3-NEXT: sra $2, $1, 24 +; +; MIPS32EB-LABEL: AtomicLoadSub8: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: lw $1, %got(y)($1) +; MIPS32EB-NEXT: addiu $2, $zero, -4 +; MIPS32EB-NEXT: and $2, $1, $2 +; MIPS32EB-NEXT: andi $1, $1, 3 +; MIPS32EB-NEXT: xori $1, $1, 3 +; MIPS32EB-NEXT: sll $3, $1, 3 +; MIPS32EB-NEXT: ori $1, $zero, 255 +; MIPS32EB-NEXT: sllv $5, $1, $3 +; MIPS32EB-NEXT: nor $6, $zero, $5 +; MIPS32EB-NEXT: sllv $4, $4, $3 +; MIPS32EB-NEXT: $BB9_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $7, 0($2) +; MIPS32EB-NEXT: subu $8, $7, $4 +; MIPS32EB-NEXT: and $8, $8, $5 +; MIPS32EB-NEXT: and $9, $7, $6 +; MIPS32EB-NEXT: or $9, $9, $8 +; MIPS32EB-NEXT: sc $9, 0($2) +; MIPS32EB-NEXT: beqz $9, $BB9_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: and $1, $7, $5 +; MIPS32EB-NEXT: srlv $1, $1, $3 +; MIPS32EB-NEXT: sll $1, $1, 24 +; MIPS32EB-NEXT: sra $1, $1, 24 +; MIPS32EB-NEXT: # %bb.3: # %entry +; MIPS32EB-NEXT: sll $1, $1, 24 +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: sra $2, $1, 24 entry: %0 = atomicrmw sub i8* @y, i8 %incr monotonic ret i8 %0 -; ALL-LABEL: AtomicLoadSub8: - -; MIPS32-ANY: lw $[[R0:[0-9]+]], %got(y) -; MIPS64-ANY: ld $[[R0:[0-9]+]], %got_disp(y)( - -; ALL: addiu $[[R1:[0-9]+]], $zero, -4 -; ALL: and $[[R2:[0-9]+]], $[[R0]], $[[R1]] -; ALL: andi $[[R3:[0-9]+]], $[[R0]], 3 -; CHECK-EL: sll $[[R5:[0-9]+]], $[[R3]], 3 -; CHECK-EB: xori $[[R4:[0-9]+]], $[[R3]], 3 -; CHECK-EB: sll $[[R5:[0-9]+]], $[[R4]], 3 -; ALL: ori $[[R6:[0-9]+]], $zero, 255 -; ALL: sllv $[[R7:[0-9]+]], $[[R6]], $[[R5]] -; ALL: nor $[[R8:[0-9]+]], $zero, $[[R7]] -; ALL: sllv $[[R9:[0-9]+]], $4, $[[R5]] - -; O0: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; O0: ld $[[R10:[0-9]+]] -; O0-NEXT: ll $[[R11:[0-9]+]], 0($[[R10]]) - -; ALL: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; ALL: ll $[[R12:[0-9]+]], 0($[[R2]]) -; ALL: subu $[[R13:[0-9]+]], $[[R12]], $[[R9]] -; ALL: and $[[R14:[0-9]+]], $[[R13]], $[[R7]] -; ALL: and $[[R15:[0-9]+]], $[[R12]], $[[R8]] -; ALL: or $[[R16:[0-9]+]], $[[R15]], $[[R14]] -; ALL: sc $[[R16]], 0($[[R2]]) -; NOT-MICROMIPS: beqz $[[R16]], [[BB0]] -; MICROMIPS: beqzc $[[R16]], [[BB0]] -; MIPSR6: beqzc $[[R16]], [[BB0]] - -; ALL: and $[[R17:[0-9]+]], $[[R12]], $[[R7]] -; ALL: srlv $[[R18:[0-9]+]], $[[R17]], $[[R5]] - -; NO-SEB-SEH: sll $[[R19:[0-9]+]], $[[R18]], 24 -; NO-SEB-SEH: sra $2, $[[R19]], 24 - -; HAS-SEB-SEH:seb $2, $[[R18]] } define signext i8 @AtomicLoadNand8(i8 signext %incr) nounwind { +; MIPS32-LABEL: AtomicLoadNand8: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: lw $1, %got(y)($1) +; MIPS32-NEXT: addiu $2, $zero, -4 +; MIPS32-NEXT: and $2, $1, $2 +; MIPS32-NEXT: andi $1, $1, 3 +; MIPS32-NEXT: sll $3, $1, 3 +; MIPS32-NEXT: ori $1, $zero, 255 +; MIPS32-NEXT: sllv $5, $1, $3 +; MIPS32-NEXT: nor $6, $zero, $5 +; MIPS32-NEXT: sllv $4, $4, $3 +; MIPS32-NEXT: $BB10_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $7, 0($2) +; MIPS32-NEXT: and $8, $7, $4 +; MIPS32-NEXT: nor $8, $zero, $8 +; MIPS32-NEXT: and $8, $8, $5 +; MIPS32-NEXT: and $9, $7, $6 +; MIPS32-NEXT: or $9, $9, $8 +; MIPS32-NEXT: sc $9, 0($2) +; MIPS32-NEXT: beqz $9, $BB10_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: and $1, $7, $5 +; MIPS32-NEXT: srlv $1, $1, $3 +; MIPS32-NEXT: sll $1, $1, 24 +; MIPS32-NEXT: sra $1, $1, 24 +; MIPS32-NEXT: # %bb.3: # %entry +; MIPS32-NEXT: sll $1, $1, 24 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: sra $2, $1, 24 +; +; MIPS32O0-LABEL: AtomicLoadNand8: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addiu $sp, $sp, -8 +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: lw $2, %got(y)($2) +; MIPS32O0-NEXT: addiu $25, $zero, -4 +; MIPS32O0-NEXT: and $25, $2, $25 +; MIPS32O0-NEXT: andi $2, $2, 3 +; MIPS32O0-NEXT: sll $2, $2, 3 +; MIPS32O0-NEXT: ori $1, $zero, 255 +; MIPS32O0-NEXT: sllv $1, $1, $2 +; MIPS32O0-NEXT: nor $3, $zero, $1 +; MIPS32O0-NEXT: sllv $4, $4, $2 +; MIPS32O0-NEXT: $BB10_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $6, 0($25) +; MIPS32O0-NEXT: and $7, $6, $4 +; MIPS32O0-NEXT: nor $7, $zero, $7 +; MIPS32O0-NEXT: and $7, $7, $1 +; MIPS32O0-NEXT: and $8, $6, $3 +; MIPS32O0-NEXT: or $8, $8, $7 +; MIPS32O0-NEXT: sc $8, 0($25) +; MIPS32O0-NEXT: beqz $8, $BB10_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: and $5, $6, $1 +; MIPS32O0-NEXT: srlv $5, $5, $2 +; MIPS32O0-NEXT: sll $5, $5, 24 +; MIPS32O0-NEXT: sra $5, $5, 24 +; MIPS32O0-NEXT: # %bb.3: # %entry +; MIPS32O0-NEXT: sw $5, 4($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: # %bb.4: # %entry +; MIPS32O0-NEXT: lw $1, 4($sp) # 4-byte Folded Reload +; MIPS32O0-NEXT: sll $2, $1, 24 +; MIPS32O0-NEXT: sra $2, $2, 24 +; MIPS32O0-NEXT: addiu $sp, $sp, 8 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicLoadNand8: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: lw $1, %got(y)($1) +; MIPS32R2-NEXT: addiu $2, $zero, -4 +; MIPS32R2-NEXT: and $2, $1, $2 +; MIPS32R2-NEXT: andi $1, $1, 3 +; MIPS32R2-NEXT: sll $3, $1, 3 +; MIPS32R2-NEXT: ori $1, $zero, 255 +; MIPS32R2-NEXT: sllv $5, $1, $3 +; MIPS32R2-NEXT: nor $6, $zero, $5 +; MIPS32R2-NEXT: sllv $4, $4, $3 +; MIPS32R2-NEXT: $BB10_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $7, 0($2) +; MIPS32R2-NEXT: and $8, $7, $4 +; MIPS32R2-NEXT: nor $8, $zero, $8 +; MIPS32R2-NEXT: and $8, $8, $5 +; MIPS32R2-NEXT: and $9, $7, $6 +; MIPS32R2-NEXT: or $9, $9, $8 +; MIPS32R2-NEXT: sc $9, 0($2) +; MIPS32R2-NEXT: beqz $9, $BB10_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: and $1, $7, $5 +; MIPS32R2-NEXT: srlv $1, $1, $3 +; MIPS32R2-NEXT: seb $1, $1 +; MIPS32R2-NEXT: # %bb.3: # %entry +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: seb $2, $1 +; +; MIPS32R6-LABEL: AtomicLoadNand8: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: lw $1, %got(y)($1) +; MIPS32R6-NEXT: addiu $2, $zero, -4 +; MIPS32R6-NEXT: and $2, $1, $2 +; MIPS32R6-NEXT: andi $1, $1, 3 +; MIPS32R6-NEXT: sll $3, $1, 3 +; MIPS32R6-NEXT: ori $1, $zero, 255 +; MIPS32R6-NEXT: sllv $5, $1, $3 +; MIPS32R6-NEXT: nor $6, $zero, $5 +; MIPS32R6-NEXT: sllv $4, $4, $3 +; MIPS32R6-NEXT: $BB10_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $7, 0($2) +; MIPS32R6-NEXT: and $8, $7, $4 +; MIPS32R6-NEXT: nor $8, $zero, $8 +; MIPS32R6-NEXT: and $8, $8, $5 +; MIPS32R6-NEXT: and $9, $7, $6 +; MIPS32R6-NEXT: or $9, $9, $8 +; MIPS32R6-NEXT: sc $9, 0($2) +; MIPS32R6-NEXT: beqzc $9, $BB10_1 +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: and $1, $7, $5 +; MIPS32R6-NEXT: srlv $1, $1, $3 +; MIPS32R6-NEXT: seb $1, $1 +; MIPS32R6-NEXT: # %bb.3: # %entry +; MIPS32R6-NEXT: jr $ra +; MIPS32R6-NEXT: seb $2, $1 +; +; MIPS32R6O0-LABEL: AtomicLoadNand8: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -8 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $4 +; MIPS32R6O0-NEXT: lw $2, %got(y)($2) +; MIPS32R6O0-NEXT: addiu $1, $zero, -4 +; MIPS32R6O0-NEXT: and $1, $2, $1 +; MIPS32R6O0-NEXT: andi $2, $2, 3 +; MIPS32R6O0-NEXT: sll $2, $2, 3 +; MIPS32R6O0-NEXT: ori $3, $zero, 255 +; MIPS32R6O0-NEXT: sllv $3, $3, $2 +; MIPS32R6O0-NEXT: nor $5, $zero, $3 +; MIPS32R6O0-NEXT: sllv $4, $4, $2 +; MIPS32R6O0-NEXT: $BB10_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $7, 0($1) +; MIPS32R6O0-NEXT: and $8, $7, $4 +; MIPS32R6O0-NEXT: nor $8, $zero, $8 +; MIPS32R6O0-NEXT: and $8, $8, $3 +; MIPS32R6O0-NEXT: and $9, $7, $5 +; MIPS32R6O0-NEXT: or $9, $9, $8 +; MIPS32R6O0-NEXT: sc $9, 0($1) +; MIPS32R6O0-NEXT: beqzc $9, $BB10_1 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: and $6, $7, $3 +; MIPS32R6O0-NEXT: srlv $6, $6, $2 +; MIPS32R6O0-NEXT: seb $6, $6 +; MIPS32R6O0-NEXT: # %bb.3: # %entry +; MIPS32R6O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $6, 0($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: # %bb.4: # %entry +; MIPS32R6O0-NEXT: lw $1, 0($sp) # 4-byte Folded Reload +; MIPS32R6O0-NEXT: seb $2, $1 +; MIPS32R6O0-NEXT: addiu $sp, $sp, 8 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicLoadNand8: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand8))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand8))) +; MIPS4-NEXT: ld $1, %got_disp(y)($1) +; MIPS4-NEXT: daddiu $2, $zero, -4 +; MIPS4-NEXT: and $2, $1, $2 +; MIPS4-NEXT: andi $1, $1, 3 +; MIPS4-NEXT: sll $3, $1, 3 +; MIPS4-NEXT: ori $1, $zero, 255 +; MIPS4-NEXT: sllv $5, $1, $3 +; MIPS4-NEXT: nor $6, $zero, $5 +; MIPS4-NEXT: sllv $4, $4, $3 +; MIPS4-NEXT: .LBB10_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $7, 0($2) +; MIPS4-NEXT: and $8, $7, $4 +; MIPS4-NEXT: nor $8, $zero, $8 +; MIPS4-NEXT: and $8, $8, $5 +; MIPS4-NEXT: and $9, $7, $6 +; MIPS4-NEXT: or $9, $9, $8 +; MIPS4-NEXT: sc $9, 0($2) +; MIPS4-NEXT: beqz $9, .LBB10_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: and $1, $7, $5 +; MIPS4-NEXT: srlv $1, $1, $3 +; MIPS4-NEXT: sll $1, $1, 24 +; MIPS4-NEXT: sra $1, $1, 24 +; MIPS4-NEXT: # %bb.3: # %entry +; MIPS4-NEXT: sll $1, $1, 24 +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: sra $2, $1, 24 +; +; MIPS64-LABEL: AtomicLoadNand8: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand8))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand8))) +; MIPS64-NEXT: ld $1, %got_disp(y)($1) +; MIPS64-NEXT: daddiu $2, $zero, -4 +; MIPS64-NEXT: and $2, $1, $2 +; MIPS64-NEXT: andi $1, $1, 3 +; MIPS64-NEXT: sll $3, $1, 3 +; MIPS64-NEXT: ori $1, $zero, 255 +; MIPS64-NEXT: sllv $5, $1, $3 +; MIPS64-NEXT: nor $6, $zero, $5 +; MIPS64-NEXT: sllv $4, $4, $3 +; MIPS64-NEXT: .LBB10_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $7, 0($2) +; MIPS64-NEXT: and $8, $7, $4 +; MIPS64-NEXT: nor $8, $zero, $8 +; MIPS64-NEXT: and $8, $8, $5 +; MIPS64-NEXT: and $9, $7, $6 +; MIPS64-NEXT: or $9, $9, $8 +; MIPS64-NEXT: sc $9, 0($2) +; MIPS64-NEXT: beqz $9, .LBB10_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: and $1, $7, $5 +; MIPS64-NEXT: srlv $1, $1, $3 +; MIPS64-NEXT: sll $1, $1, 24 +; MIPS64-NEXT: sra $1, $1, 24 +; MIPS64-NEXT: # %bb.3: # %entry +; MIPS64-NEXT: sll $1, $1, 24 +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: sra $2, $1, 24 +; +; MIPS64R2-LABEL: AtomicLoadNand8: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand8))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand8))) +; MIPS64R2-NEXT: ld $1, %got_disp(y)($1) +; MIPS64R2-NEXT: daddiu $2, $zero, -4 +; MIPS64R2-NEXT: and $2, $1, $2 +; MIPS64R2-NEXT: andi $1, $1, 3 +; MIPS64R2-NEXT: sll $3, $1, 3 +; MIPS64R2-NEXT: ori $1, $zero, 255 +; MIPS64R2-NEXT: sllv $5, $1, $3 +; MIPS64R2-NEXT: nor $6, $zero, $5 +; MIPS64R2-NEXT: sllv $4, $4, $3 +; MIPS64R2-NEXT: .LBB10_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $7, 0($2) +; MIPS64R2-NEXT: and $8, $7, $4 +; MIPS64R2-NEXT: nor $8, $zero, $8 +; MIPS64R2-NEXT: and $8, $8, $5 +; MIPS64R2-NEXT: and $9, $7, $6 +; MIPS64R2-NEXT: or $9, $9, $8 +; MIPS64R2-NEXT: sc $9, 0($2) +; MIPS64R2-NEXT: beqz $9, .LBB10_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: and $1, $7, $5 +; MIPS64R2-NEXT: srlv $1, $1, $3 +; MIPS64R2-NEXT: seb $1, $1 +; MIPS64R2-NEXT: # %bb.3: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: seb $2, $1 +; +; MIPS64R6-LABEL: AtomicLoadNand8: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand8))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand8))) +; MIPS64R6-NEXT: ld $1, %got_disp(y)($1) +; MIPS64R6-NEXT: daddiu $2, $zero, -4 +; MIPS64R6-NEXT: and $2, $1, $2 +; MIPS64R6-NEXT: andi $1, $1, 3 +; MIPS64R6-NEXT: sll $3, $1, 3 +; MIPS64R6-NEXT: ori $1, $zero, 255 +; MIPS64R6-NEXT: sllv $5, $1, $3 +; MIPS64R6-NEXT: nor $6, $zero, $5 +; MIPS64R6-NEXT: sllv $4, $4, $3 +; MIPS64R6-NEXT: .LBB10_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $7, 0($2) +; MIPS64R6-NEXT: and $8, $7, $4 +; MIPS64R6-NEXT: nor $8, $zero, $8 +; MIPS64R6-NEXT: and $8, $8, $5 +; MIPS64R6-NEXT: and $9, $7, $6 +; MIPS64R6-NEXT: or $9, $9, $8 +; MIPS64R6-NEXT: sc $9, 0($2) +; MIPS64R6-NEXT: beqzc $9, .LBB10_1 +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: and $1, $7, $5 +; MIPS64R6-NEXT: srlv $1, $1, $3 +; MIPS64R6-NEXT: seb $1, $1 +; MIPS64R6-NEXT: # %bb.3: # %entry +; MIPS64R6-NEXT: jr $ra +; MIPS64R6-NEXT: seb $2, $1 +; +; MIPS64R6O0-LABEL: AtomicLoadNand8: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand8))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand8))) +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(y)($1) +; MIPS64R6O0-NEXT: daddiu $4, $zero, -4 +; MIPS64R6O0-NEXT: and $4, $1, $4 +; MIPS64R6O0-NEXT: andi $3, $1, 3 +; MIPS64R6O0-NEXT: xori $3, $3, 3 +; MIPS64R6O0-NEXT: sll $3, $3, 3 +; MIPS64R6O0-NEXT: ori $5, $zero, 255 +; MIPS64R6O0-NEXT: sllv $5, $5, $3 +; MIPS64R6O0-NEXT: nor $6, $zero, $5 +; MIPS64R6O0-NEXT: sllv $2, $2, $3 +; MIPS64R6O0-NEXT: .LBB10_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $8, 0($4) +; MIPS64R6O0-NEXT: and $9, $8, $2 +; MIPS64R6O0-NEXT: nor $9, $zero, $9 +; MIPS64R6O0-NEXT: and $9, $9, $5 +; MIPS64R6O0-NEXT: and $10, $8, $6 +; MIPS64R6O0-NEXT: or $10, $10, $9 +; MIPS64R6O0-NEXT: sc $10, 0($4) +; MIPS64R6O0-NEXT: beqzc $10, .LBB10_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: and $7, $8, $5 +; MIPS64R6O0-NEXT: srlv $7, $7, $3 +; MIPS64R6O0-NEXT: seb $7, $7 +; MIPS64R6O0-NEXT: # %bb.3: # %entry +; MIPS64R6O0-NEXT: sw $7, 12($sp) # 4-byte Folded Spill +; MIPS64R6O0-NEXT: # %bb.4: # %entry +; MIPS64R6O0-NEXT: lw $1, 12($sp) # 4-byte Folded Reload +; MIPS64R6O0-NEXT: seb $2, $1 +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicLoadNand8: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: lw $1, %got(y)($2) +; MM32-NEXT: addiu $2, $zero, -4 +; MM32-NEXT: and $2, $1, $2 +; MM32-NEXT: andi $1, $1, 3 +; MM32-NEXT: sll $3, $1, 3 +; MM32-NEXT: ori $1, $zero, 255 +; MM32-NEXT: sllv $5, $1, $3 +; MM32-NEXT: nor $6, $zero, $5 +; MM32-NEXT: sllv $4, $4, $3 +; MM32-NEXT: $BB10_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $7, 0($2) +; MM32-NEXT: and $8, $7, $4 +; MM32-NEXT: nor $8, $zero, $8 +; MM32-NEXT: and $8, $8, $5 +; MM32-NEXT: and $9, $7, $6 +; MM32-NEXT: or $9, $9, $8 +; MM32-NEXT: sc $9, 0($2) +; MM32-NEXT: beqzc $9, $BB10_1 +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: and $1, $7, $5 +; MM32-NEXT: srlv $1, $1, $3 +; MM32-NEXT: seb $1, $1 +; MM32-NEXT: # %bb.3: # %entry +; MM32-NEXT: jr $ra +; MM32-NEXT: seb $2, $1 +; +; O1-LABEL: AtomicLoadNand8: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: lw $1, %got(y)($1) +; O1-NEXT: addiu $2, $zero, -4 +; O1-NEXT: and $2, $1, $2 +; O1-NEXT: andi $1, $1, 3 +; O1-NEXT: sll $3, $1, 3 +; O1-NEXT: ori $1, $zero, 255 +; O1-NEXT: sllv $5, $1, $3 +; O1-NEXT: nor $6, $zero, $5 +; O1-NEXT: sllv $4, $4, $3 +; O1-NEXT: $BB10_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $7, 0($2) +; O1-NEXT: and $8, $7, $4 +; O1-NEXT: nor $8, $zero, $8 +; O1-NEXT: and $8, $8, $5 +; O1-NEXT: and $9, $7, $6 +; O1-NEXT: or $9, $9, $8 +; O1-NEXT: sc $9, 0($2) +; O1-NEXT: beqz $9, $BB10_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: and $1, $7, $5 +; O1-NEXT: srlv $1, $1, $3 +; O1-NEXT: sll $1, $1, 24 +; O1-NEXT: sra $1, $1, 24 +; O1-NEXT: # %bb.3: # %entry +; O1-NEXT: sll $1, $1, 24 +; O1-NEXT: jr $ra +; O1-NEXT: sra $2, $1, 24 +; +; O2-LABEL: AtomicLoadNand8: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: lw $1, %got(y)($1) +; O2-NEXT: addiu $2, $zero, -4 +; O2-NEXT: and $2, $1, $2 +; O2-NEXT: andi $1, $1, 3 +; O2-NEXT: sll $3, $1, 3 +; O2-NEXT: ori $1, $zero, 255 +; O2-NEXT: sllv $5, $1, $3 +; O2-NEXT: nor $6, $zero, $5 +; O2-NEXT: sllv $4, $4, $3 +; O2-NEXT: $BB10_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $7, 0($2) +; O2-NEXT: and $8, $7, $4 +; O2-NEXT: nor $8, $zero, $8 +; O2-NEXT: and $8, $8, $5 +; O2-NEXT: and $9, $7, $6 +; O2-NEXT: or $9, $9, $8 +; O2-NEXT: sc $9, 0($2) +; O2-NEXT: beqz $9, $BB10_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: and $1, $7, $5 +; O2-NEXT: srlv $1, $1, $3 +; O2-NEXT: sll $1, $1, 24 +; O2-NEXT: sra $1, $1, 24 +; O2-NEXT: # %bb.3: # %entry +; O2-NEXT: sll $1, $1, 24 +; O2-NEXT: jr $ra +; O2-NEXT: sra $2, $1, 24 +; +; O3-LABEL: AtomicLoadNand8: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: addiu $2, $zero, -4 +; O3-NEXT: lw $1, %got(y)($1) +; O3-NEXT: and $2, $1, $2 +; O3-NEXT: andi $1, $1, 3 +; O3-NEXT: sll $3, $1, 3 +; O3-NEXT: ori $1, $zero, 255 +; O3-NEXT: sllv $5, $1, $3 +; O3-NEXT: sllv $4, $4, $3 +; O3-NEXT: nor $6, $zero, $5 +; O3-NEXT: $BB10_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $7, 0($2) +; O3-NEXT: and $8, $7, $4 +; O3-NEXT: nor $8, $zero, $8 +; O3-NEXT: and $8, $8, $5 +; O3-NEXT: and $9, $7, $6 +; O3-NEXT: or $9, $9, $8 +; O3-NEXT: sc $9, 0($2) +; O3-NEXT: beqz $9, $BB10_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: and $1, $7, $5 +; O3-NEXT: srlv $1, $1, $3 +; O3-NEXT: sll $1, $1, 24 +; O3-NEXT: sra $1, $1, 24 +; O3-NEXT: # %bb.3: # %entry +; O3-NEXT: sll $1, $1, 24 +; O3-NEXT: jr $ra +; O3-NEXT: sra $2, $1, 24 +; +; MIPS32EB-LABEL: AtomicLoadNand8: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: lw $1, %got(y)($1) +; MIPS32EB-NEXT: addiu $2, $zero, -4 +; MIPS32EB-NEXT: and $2, $1, $2 +; MIPS32EB-NEXT: andi $1, $1, 3 +; MIPS32EB-NEXT: xori $1, $1, 3 +; MIPS32EB-NEXT: sll $3, $1, 3 +; MIPS32EB-NEXT: ori $1, $zero, 255 +; MIPS32EB-NEXT: sllv $5, $1, $3 +; MIPS32EB-NEXT: nor $6, $zero, $5 +; MIPS32EB-NEXT: sllv $4, $4, $3 +; MIPS32EB-NEXT: $BB10_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $7, 0($2) +; MIPS32EB-NEXT: and $8, $7, $4 +; MIPS32EB-NEXT: nor $8, $zero, $8 +; MIPS32EB-NEXT: and $8, $8, $5 +; MIPS32EB-NEXT: and $9, $7, $6 +; MIPS32EB-NEXT: or $9, $9, $8 +; MIPS32EB-NEXT: sc $9, 0($2) +; MIPS32EB-NEXT: beqz $9, $BB10_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: and $1, $7, $5 +; MIPS32EB-NEXT: srlv $1, $1, $3 +; MIPS32EB-NEXT: sll $1, $1, 24 +; MIPS32EB-NEXT: sra $1, $1, 24 +; MIPS32EB-NEXT: # %bb.3: # %entry +; MIPS32EB-NEXT: sll $1, $1, 24 +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: sra $2, $1, 24 entry: %0 = atomicrmw nand i8* @y, i8 %incr monotonic ret i8 %0 -; ALL-LABEL: AtomicLoadNand8: - -; MIPS32-ANY: lw $[[R0:[0-9]+]], %got(y) -; MIPS64-ANY: ld $[[R0:[0-9]+]], %got_disp(y)( - -; ALL: addiu $[[R1:[0-9]+]], $zero, -4 -; ALL: and $[[R2:[0-9]+]], $[[R0]], $[[R1]] -; ALL: andi $[[R3:[0-9]+]], $[[R0]], 3 -; CHECK-EL: sll $[[R5:[0-9]+]], $[[R3]], 3 -; CHECK-EB: xori $[[R4:[0-9]+]], $[[R3]], 3 -; CHECK-EB: sll $[[R5:[0-9]+]], $[[R4]], 3 -; ALL: ori $[[R6:[0-9]+]], $zero, 255 -; ALL: sllv $[[R7:[0-9]+]], $[[R6]], $[[R5]] -; ALL: nor $[[R8:[0-9]+]], $zero, $[[R7]] -; ALL: sllv $[[R9:[0-9]+]], $4, $[[R5]] - -; O0: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; O0: ld $[[R10:[0-9]+]] -; O0-NEXT: ll $[[R11:[0-9]+]], 0($[[R10]]) - -; ALL: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; ALL: ll $[[R12:[0-9]+]], 0($[[R2]]) -; ALL: and $[[R13:[0-9]+]], $[[R12]], $[[R9]] -; ALL: nor $[[R14:[0-9]+]], $zero, $[[R13]] -; ALL: and $[[R15:[0-9]+]], $[[R14]], $[[R7]] -; ALL: and $[[R16:[0-9]+]], $[[R12]], $[[R8]] -; ALL: or $[[R17:[0-9]+]], $[[R16]], $[[R15]] -; ALL: sc $[[R17]], 0($[[R2]]) -; NOT-MICROMIPS: beqz $[[R17]], [[BB0]] -; MICROMIPS: beqzc $[[R17]], [[BB0]] -; MIPSR6: beqzc $[[R17]], [[BB0]] - -; ALL: and $[[R18:[0-9]+]], $[[R12]], $[[R7]] -; ALL: srlv $[[R19:[0-9]+]], $[[R18]], $[[R5]] - -; NO-SEB-SEH: sll $[[R20:[0-9]+]], $[[R19]], 24 -; NO-SEB-SEH: sra $2, $[[R20]], 24 - -; HAS-SEB-SEH: seb $2, $[[R19]] } define signext i8 @AtomicSwap8(i8 signext %newval) nounwind { +; MIPS32-LABEL: AtomicSwap8: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: lw $1, %got(y)($1) +; MIPS32-NEXT: addiu $2, $zero, -4 +; MIPS32-NEXT: and $2, $1, $2 +; MIPS32-NEXT: andi $1, $1, 3 +; MIPS32-NEXT: sll $3, $1, 3 +; MIPS32-NEXT: ori $1, $zero, 255 +; MIPS32-NEXT: sllv $5, $1, $3 +; MIPS32-NEXT: nor $6, $zero, $5 +; MIPS32-NEXT: sllv $4, $4, $3 +; MIPS32-NEXT: $BB11_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $7, 0($2) +; MIPS32-NEXT: and $8, $4, $5 +; MIPS32-NEXT: and $9, $7, $6 +; MIPS32-NEXT: or $9, $9, $8 +; MIPS32-NEXT: sc $9, 0($2) +; MIPS32-NEXT: beqz $9, $BB11_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: and $1, $7, $5 +; MIPS32-NEXT: srlv $1, $1, $3 +; MIPS32-NEXT: sll $1, $1, 24 +; MIPS32-NEXT: sra $1, $1, 24 +; MIPS32-NEXT: # %bb.3: # %entry +; MIPS32-NEXT: sll $1, $1, 24 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: sra $2, $1, 24 +; +; MIPS32O0-LABEL: AtomicSwap8: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addiu $sp, $sp, -8 +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: lw $2, %got(y)($2) +; MIPS32O0-NEXT: addiu $25, $zero, -4 +; MIPS32O0-NEXT: and $25, $2, $25 +; MIPS32O0-NEXT: andi $2, $2, 3 +; MIPS32O0-NEXT: sll $2, $2, 3 +; MIPS32O0-NEXT: ori $1, $zero, 255 +; MIPS32O0-NEXT: sllv $1, $1, $2 +; MIPS32O0-NEXT: nor $3, $zero, $1 +; MIPS32O0-NEXT: sllv $4, $4, $2 +; MIPS32O0-NEXT: $BB11_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $6, 0($25) +; MIPS32O0-NEXT: and $7, $4, $1 +; MIPS32O0-NEXT: and $8, $6, $3 +; MIPS32O0-NEXT: or $8, $8, $7 +; MIPS32O0-NEXT: sc $8, 0($25) +; MIPS32O0-NEXT: beqz $8, $BB11_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: and $5, $6, $1 +; MIPS32O0-NEXT: srlv $5, $5, $2 +; MIPS32O0-NEXT: sll $5, $5, 24 +; MIPS32O0-NEXT: sra $5, $5, 24 +; MIPS32O0-NEXT: # %bb.3: # %entry +; MIPS32O0-NEXT: sw $5, 4($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: # %bb.4: # %entry +; MIPS32O0-NEXT: lw $1, 4($sp) # 4-byte Folded Reload +; MIPS32O0-NEXT: sll $2, $1, 24 +; MIPS32O0-NEXT: sra $2, $2, 24 +; MIPS32O0-NEXT: addiu $sp, $sp, 8 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicSwap8: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: lw $1, %got(y)($1) +; MIPS32R2-NEXT: addiu $2, $zero, -4 +; MIPS32R2-NEXT: and $2, $1, $2 +; MIPS32R2-NEXT: andi $1, $1, 3 +; MIPS32R2-NEXT: sll $3, $1, 3 +; MIPS32R2-NEXT: ori $1, $zero, 255 +; MIPS32R2-NEXT: sllv $5, $1, $3 +; MIPS32R2-NEXT: nor $6, $zero, $5 +; MIPS32R2-NEXT: sllv $4, $4, $3 +; MIPS32R2-NEXT: $BB11_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $7, 0($2) +; MIPS32R2-NEXT: and $8, $4, $5 +; MIPS32R2-NEXT: and $9, $7, $6 +; MIPS32R2-NEXT: or $9, $9, $8 +; MIPS32R2-NEXT: sc $9, 0($2) +; MIPS32R2-NEXT: beqz $9, $BB11_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: and $1, $7, $5 +; MIPS32R2-NEXT: srlv $1, $1, $3 +; MIPS32R2-NEXT: seb $1, $1 +; MIPS32R2-NEXT: # %bb.3: # %entry +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: seb $2, $1 +; +; MIPS32R6-LABEL: AtomicSwap8: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: lw $1, %got(y)($1) +; MIPS32R6-NEXT: addiu $2, $zero, -4 +; MIPS32R6-NEXT: and $2, $1, $2 +; MIPS32R6-NEXT: andi $1, $1, 3 +; MIPS32R6-NEXT: sll $3, $1, 3 +; MIPS32R6-NEXT: ori $1, $zero, 255 +; MIPS32R6-NEXT: sllv $5, $1, $3 +; MIPS32R6-NEXT: nor $6, $zero, $5 +; MIPS32R6-NEXT: sllv $4, $4, $3 +; MIPS32R6-NEXT: $BB11_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $7, 0($2) +; MIPS32R6-NEXT: and $8, $4, $5 +; MIPS32R6-NEXT: and $9, $7, $6 +; MIPS32R6-NEXT: or $9, $9, $8 +; MIPS32R6-NEXT: sc $9, 0($2) +; MIPS32R6-NEXT: beqzc $9, $BB11_1 +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: and $1, $7, $5 +; MIPS32R6-NEXT: srlv $1, $1, $3 +; MIPS32R6-NEXT: seb $1, $1 +; MIPS32R6-NEXT: # %bb.3: # %entry +; MIPS32R6-NEXT: jr $ra +; MIPS32R6-NEXT: seb $2, $1 +; +; MIPS32R6O0-LABEL: AtomicSwap8: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -8 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $4 +; MIPS32R6O0-NEXT: lw $2, %got(y)($2) +; MIPS32R6O0-NEXT: addiu $1, $zero, -4 +; MIPS32R6O0-NEXT: and $1, $2, $1 +; MIPS32R6O0-NEXT: andi $2, $2, 3 +; MIPS32R6O0-NEXT: sll $2, $2, 3 +; MIPS32R6O0-NEXT: ori $3, $zero, 255 +; MIPS32R6O0-NEXT: sllv $3, $3, $2 +; MIPS32R6O0-NEXT: nor $5, $zero, $3 +; MIPS32R6O0-NEXT: sllv $4, $4, $2 +; MIPS32R6O0-NEXT: $BB11_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $7, 0($1) +; MIPS32R6O0-NEXT: and $8, $4, $3 +; MIPS32R6O0-NEXT: and $9, $7, $5 +; MIPS32R6O0-NEXT: or $9, $9, $8 +; MIPS32R6O0-NEXT: sc $9, 0($1) +; MIPS32R6O0-NEXT: beqzc $9, $BB11_1 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: and $6, $7, $3 +; MIPS32R6O0-NEXT: srlv $6, $6, $2 +; MIPS32R6O0-NEXT: seb $6, $6 +; MIPS32R6O0-NEXT: # %bb.3: # %entry +; MIPS32R6O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $6, 0($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: # %bb.4: # %entry +; MIPS32R6O0-NEXT: lw $1, 0($sp) # 4-byte Folded Reload +; MIPS32R6O0-NEXT: seb $2, $1 +; MIPS32R6O0-NEXT: addiu $sp, $sp, 8 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicSwap8: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap8))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap8))) +; MIPS4-NEXT: ld $1, %got_disp(y)($1) +; MIPS4-NEXT: daddiu $2, $zero, -4 +; MIPS4-NEXT: and $2, $1, $2 +; MIPS4-NEXT: andi $1, $1, 3 +; MIPS4-NEXT: sll $3, $1, 3 +; MIPS4-NEXT: ori $1, $zero, 255 +; MIPS4-NEXT: sllv $5, $1, $3 +; MIPS4-NEXT: nor $6, $zero, $5 +; MIPS4-NEXT: sllv $4, $4, $3 +; MIPS4-NEXT: .LBB11_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $7, 0($2) +; MIPS4-NEXT: and $8, $4, $5 +; MIPS4-NEXT: and $9, $7, $6 +; MIPS4-NEXT: or $9, $9, $8 +; MIPS4-NEXT: sc $9, 0($2) +; MIPS4-NEXT: beqz $9, .LBB11_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: and $1, $7, $5 +; MIPS4-NEXT: srlv $1, $1, $3 +; MIPS4-NEXT: sll $1, $1, 24 +; MIPS4-NEXT: sra $1, $1, 24 +; MIPS4-NEXT: # %bb.3: # %entry +; MIPS4-NEXT: sll $1, $1, 24 +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: sra $2, $1, 24 +; +; MIPS64-LABEL: AtomicSwap8: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap8))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap8))) +; MIPS64-NEXT: ld $1, %got_disp(y)($1) +; MIPS64-NEXT: daddiu $2, $zero, -4 +; MIPS64-NEXT: and $2, $1, $2 +; MIPS64-NEXT: andi $1, $1, 3 +; MIPS64-NEXT: sll $3, $1, 3 +; MIPS64-NEXT: ori $1, $zero, 255 +; MIPS64-NEXT: sllv $5, $1, $3 +; MIPS64-NEXT: nor $6, $zero, $5 +; MIPS64-NEXT: sllv $4, $4, $3 +; MIPS64-NEXT: .LBB11_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $7, 0($2) +; MIPS64-NEXT: and $8, $4, $5 +; MIPS64-NEXT: and $9, $7, $6 +; MIPS64-NEXT: or $9, $9, $8 +; MIPS64-NEXT: sc $9, 0($2) +; MIPS64-NEXT: beqz $9, .LBB11_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: and $1, $7, $5 +; MIPS64-NEXT: srlv $1, $1, $3 +; MIPS64-NEXT: sll $1, $1, 24 +; MIPS64-NEXT: sra $1, $1, 24 +; MIPS64-NEXT: # %bb.3: # %entry +; MIPS64-NEXT: sll $1, $1, 24 +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: sra $2, $1, 24 +; +; MIPS64R2-LABEL: AtomicSwap8: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap8))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap8))) +; MIPS64R2-NEXT: ld $1, %got_disp(y)($1) +; MIPS64R2-NEXT: daddiu $2, $zero, -4 +; MIPS64R2-NEXT: and $2, $1, $2 +; MIPS64R2-NEXT: andi $1, $1, 3 +; MIPS64R2-NEXT: sll $3, $1, 3 +; MIPS64R2-NEXT: ori $1, $zero, 255 +; MIPS64R2-NEXT: sllv $5, $1, $3 +; MIPS64R2-NEXT: nor $6, $zero, $5 +; MIPS64R2-NEXT: sllv $4, $4, $3 +; MIPS64R2-NEXT: .LBB11_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $7, 0($2) +; MIPS64R2-NEXT: and $8, $4, $5 +; MIPS64R2-NEXT: and $9, $7, $6 +; MIPS64R2-NEXT: or $9, $9, $8 +; MIPS64R2-NEXT: sc $9, 0($2) +; MIPS64R2-NEXT: beqz $9, .LBB11_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: and $1, $7, $5 +; MIPS64R2-NEXT: srlv $1, $1, $3 +; MIPS64R2-NEXT: seb $1, $1 +; MIPS64R2-NEXT: # %bb.3: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: seb $2, $1 +; +; MIPS64R6-LABEL: AtomicSwap8: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap8))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap8))) +; MIPS64R6-NEXT: ld $1, %got_disp(y)($1) +; MIPS64R6-NEXT: daddiu $2, $zero, -4 +; MIPS64R6-NEXT: and $2, $1, $2 +; MIPS64R6-NEXT: andi $1, $1, 3 +; MIPS64R6-NEXT: sll $3, $1, 3 +; MIPS64R6-NEXT: ori $1, $zero, 255 +; MIPS64R6-NEXT: sllv $5, $1, $3 +; MIPS64R6-NEXT: nor $6, $zero, $5 +; MIPS64R6-NEXT: sllv $4, $4, $3 +; MIPS64R6-NEXT: .LBB11_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $7, 0($2) +; MIPS64R6-NEXT: and $8, $4, $5 +; MIPS64R6-NEXT: and $9, $7, $6 +; MIPS64R6-NEXT: or $9, $9, $8 +; MIPS64R6-NEXT: sc $9, 0($2) +; MIPS64R6-NEXT: beqzc $9, .LBB11_1 +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: and $1, $7, $5 +; MIPS64R6-NEXT: srlv $1, $1, $3 +; MIPS64R6-NEXT: seb $1, $1 +; MIPS64R6-NEXT: # %bb.3: # %entry +; MIPS64R6-NEXT: jr $ra +; MIPS64R6-NEXT: seb $2, $1 +; +; MIPS64R6O0-LABEL: AtomicSwap8: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap8))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap8))) +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(y)($1) +; MIPS64R6O0-NEXT: daddiu $4, $zero, -4 +; MIPS64R6O0-NEXT: and $4, $1, $4 +; MIPS64R6O0-NEXT: andi $3, $1, 3 +; MIPS64R6O0-NEXT: xori $3, $3, 3 +; MIPS64R6O0-NEXT: sll $3, $3, 3 +; MIPS64R6O0-NEXT: ori $5, $zero, 255 +; MIPS64R6O0-NEXT: sllv $5, $5, $3 +; MIPS64R6O0-NEXT: nor $6, $zero, $5 +; MIPS64R6O0-NEXT: sllv $2, $2, $3 +; MIPS64R6O0-NEXT: .LBB11_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $8, 0($4) +; MIPS64R6O0-NEXT: and $9, $2, $5 +; MIPS64R6O0-NEXT: and $10, $8, $6 +; MIPS64R6O0-NEXT: or $10, $10, $9 +; MIPS64R6O0-NEXT: sc $10, 0($4) +; MIPS64R6O0-NEXT: beqzc $10, .LBB11_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: and $7, $8, $5 +; MIPS64R6O0-NEXT: srlv $7, $7, $3 +; MIPS64R6O0-NEXT: seb $7, $7 +; MIPS64R6O0-NEXT: # %bb.3: # %entry +; MIPS64R6O0-NEXT: sw $7, 12($sp) # 4-byte Folded Spill +; MIPS64R6O0-NEXT: # %bb.4: # %entry +; MIPS64R6O0-NEXT: lw $1, 12($sp) # 4-byte Folded Reload +; MIPS64R6O0-NEXT: seb $2, $1 +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicSwap8: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: lw $1, %got(y)($2) +; MM32-NEXT: addiu $2, $zero, -4 +; MM32-NEXT: and $2, $1, $2 +; MM32-NEXT: andi $1, $1, 3 +; MM32-NEXT: sll $3, $1, 3 +; MM32-NEXT: ori $1, $zero, 255 +; MM32-NEXT: sllv $5, $1, $3 +; MM32-NEXT: nor $6, $zero, $5 +; MM32-NEXT: sllv $4, $4, $3 +; MM32-NEXT: $BB11_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $7, 0($2) +; MM32-NEXT: and $8, $4, $5 +; MM32-NEXT: and $9, $7, $6 +; MM32-NEXT: or $9, $9, $8 +; MM32-NEXT: sc $9, 0($2) +; MM32-NEXT: beqzc $9, $BB11_1 +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: and $1, $7, $5 +; MM32-NEXT: srlv $1, $1, $3 +; MM32-NEXT: seb $1, $1 +; MM32-NEXT: # %bb.3: # %entry +; MM32-NEXT: jr $ra +; MM32-NEXT: seb $2, $1 +; +; O1-LABEL: AtomicSwap8: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: lw $1, %got(y)($1) +; O1-NEXT: addiu $2, $zero, -4 +; O1-NEXT: and $2, $1, $2 +; O1-NEXT: andi $1, $1, 3 +; O1-NEXT: sll $3, $1, 3 +; O1-NEXT: ori $1, $zero, 255 +; O1-NEXT: sllv $5, $1, $3 +; O1-NEXT: nor $6, $zero, $5 +; O1-NEXT: sllv $4, $4, $3 +; O1-NEXT: $BB11_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $7, 0($2) +; O1-NEXT: and $8, $4, $5 +; O1-NEXT: and $9, $7, $6 +; O1-NEXT: or $9, $9, $8 +; O1-NEXT: sc $9, 0($2) +; O1-NEXT: beqz $9, $BB11_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: and $1, $7, $5 +; O1-NEXT: srlv $1, $1, $3 +; O1-NEXT: sll $1, $1, 24 +; O1-NEXT: sra $1, $1, 24 +; O1-NEXT: # %bb.3: # %entry +; O1-NEXT: sll $1, $1, 24 +; O1-NEXT: jr $ra +; O1-NEXT: sra $2, $1, 24 +; +; O2-LABEL: AtomicSwap8: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: lw $1, %got(y)($1) +; O2-NEXT: addiu $2, $zero, -4 +; O2-NEXT: and $2, $1, $2 +; O2-NEXT: andi $1, $1, 3 +; O2-NEXT: sll $3, $1, 3 +; O2-NEXT: ori $1, $zero, 255 +; O2-NEXT: sllv $5, $1, $3 +; O2-NEXT: nor $6, $zero, $5 +; O2-NEXT: sllv $4, $4, $3 +; O2-NEXT: $BB11_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $7, 0($2) +; O2-NEXT: and $8, $4, $5 +; O2-NEXT: and $9, $7, $6 +; O2-NEXT: or $9, $9, $8 +; O2-NEXT: sc $9, 0($2) +; O2-NEXT: beqz $9, $BB11_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: and $1, $7, $5 +; O2-NEXT: srlv $1, $1, $3 +; O2-NEXT: sll $1, $1, 24 +; O2-NEXT: sra $1, $1, 24 +; O2-NEXT: # %bb.3: # %entry +; O2-NEXT: sll $1, $1, 24 +; O2-NEXT: jr $ra +; O2-NEXT: sra $2, $1, 24 +; +; O3-LABEL: AtomicSwap8: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: addiu $2, $zero, -4 +; O3-NEXT: lw $1, %got(y)($1) +; O3-NEXT: and $2, $1, $2 +; O3-NEXT: andi $1, $1, 3 +; O3-NEXT: sll $3, $1, 3 +; O3-NEXT: ori $1, $zero, 255 +; O3-NEXT: sllv $5, $1, $3 +; O3-NEXT: sllv $4, $4, $3 +; O3-NEXT: nor $6, $zero, $5 +; O3-NEXT: $BB11_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $7, 0($2) +; O3-NEXT: and $8, $4, $5 +; O3-NEXT: and $9, $7, $6 +; O3-NEXT: or $9, $9, $8 +; O3-NEXT: sc $9, 0($2) +; O3-NEXT: beqz $9, $BB11_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: and $1, $7, $5 +; O3-NEXT: srlv $1, $1, $3 +; O3-NEXT: sll $1, $1, 24 +; O3-NEXT: sra $1, $1, 24 +; O3-NEXT: # %bb.3: # %entry +; O3-NEXT: sll $1, $1, 24 +; O3-NEXT: jr $ra +; O3-NEXT: sra $2, $1, 24 +; +; MIPS32EB-LABEL: AtomicSwap8: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: lw $1, %got(y)($1) +; MIPS32EB-NEXT: addiu $2, $zero, -4 +; MIPS32EB-NEXT: and $2, $1, $2 +; MIPS32EB-NEXT: andi $1, $1, 3 +; MIPS32EB-NEXT: xori $1, $1, 3 +; MIPS32EB-NEXT: sll $3, $1, 3 +; MIPS32EB-NEXT: ori $1, $zero, 255 +; MIPS32EB-NEXT: sllv $5, $1, $3 +; MIPS32EB-NEXT: nor $6, $zero, $5 +; MIPS32EB-NEXT: sllv $4, $4, $3 +; MIPS32EB-NEXT: $BB11_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $7, 0($2) +; MIPS32EB-NEXT: and $8, $4, $5 +; MIPS32EB-NEXT: and $9, $7, $6 +; MIPS32EB-NEXT: or $9, $9, $8 +; MIPS32EB-NEXT: sc $9, 0($2) +; MIPS32EB-NEXT: beqz $9, $BB11_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: and $1, $7, $5 +; MIPS32EB-NEXT: srlv $1, $1, $3 +; MIPS32EB-NEXT: sll $1, $1, 24 +; MIPS32EB-NEXT: sra $1, $1, 24 +; MIPS32EB-NEXT: # %bb.3: # %entry +; MIPS32EB-NEXT: sll $1, $1, 24 +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: sra $2, $1, 24 entry: %0 = atomicrmw xchg i8* @y, i8 %newval monotonic ret i8 %0 - -; ALL-LABEL: AtomicSwap8: - -; MIPS32-ANY: lw $[[R0:[0-9]+]], %got(y) -; MIPS64-ANY: ld $[[R0:[0-9]+]], %got_disp(y)( - -; ALL: addiu $[[R1:[0-9]+]], $zero, -4 -; ALL: and $[[R2:[0-9]+]], $[[R0]], $[[R1]] -; ALL: andi $[[R3:[0-9]+]], $[[R0]], 3 -; CHECK-EL: sll $[[R5:[0-9]+]], $[[R3]], 3 -; CHECK-EB: xori $[[R4:[0-9]+]], $[[R3]], 3 -; CHECK-EB: sll $[[R5:[0-9]+]], $[[R4]], 3 -; ALL: ori $[[R6:[0-9]+]], $zero, 255 -; ALL: sllv $[[R7:[0-9]+]], $[[R6]], $[[R5]] -; ALL: nor $[[R8:[0-9]+]], $zero, $[[R7]] -; ALL: sllv $[[R9:[0-9]+]], $4, $[[R5]] - -; ALL: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; ALL: ll $[[R10:[0-9]+]], 0($[[R2]]) -; ALL: and $[[R18:[0-9]+]], $[[R9]], $[[R7]] -; ALL: and $[[R13:[0-9]+]], $[[R10]], $[[R8]] -; ALL: or $[[R14:[0-9]+]], $[[R13]], $[[R18]] -; ALL: sc $[[R14]], 0($[[R2]]) -; NOT-MICROMIPS: beqz $[[R14]], [[BB0]] -; MICROMIPS: beqzc $[[R14]], [[BB0]] -; MIPSR6: beqzc $[[R14]], [[BB0]] - -; ALL: and $[[R15:[0-9]+]], $[[R10]], $[[R7]] -; ALL: srlv $[[R16:[0-9]+]], $[[R15]], $[[R5]] - -; NO-SEB-SEH: sll $[[R17:[0-9]+]], $[[R16]], 24 -; NO-SEB-SEH: sra $2, $[[R17]], 24 - -; HAS-SEB-SEH: seb $2, $[[R16]] - } define signext i8 @AtomicCmpSwap8(i8 signext %oldval, i8 signext %newval) nounwind { +; MIPS32-LABEL: AtomicCmpSwap8: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: lw $1, %got(y)($1) +; MIPS32-NEXT: addiu $2, $zero, -4 +; MIPS32-NEXT: and $2, $1, $2 +; MIPS32-NEXT: andi $1, $1, 3 +; MIPS32-NEXT: sll $3, $1, 3 +; MIPS32-NEXT: ori $1, $zero, 255 +; MIPS32-NEXT: sllv $6, $1, $3 +; MIPS32-NEXT: nor $7, $zero, $6 +; MIPS32-NEXT: andi $1, $4, 255 +; MIPS32-NEXT: sllv $4, $1, $3 +; MIPS32-NEXT: andi $1, $5, 255 +; MIPS32-NEXT: sllv $5, $1, $3 +; MIPS32-NEXT: $BB12_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $8, 0($2) +; MIPS32-NEXT: and $9, $8, $6 +; MIPS32-NEXT: bne $9, $4, $BB12_3 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: # in Loop: Header=BB12_1 Depth=1 +; MIPS32-NEXT: and $8, $8, $7 +; MIPS32-NEXT: or $8, $8, $5 +; MIPS32-NEXT: sc $8, 0($2) +; MIPS32-NEXT: beqz $8, $BB12_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: $BB12_3: # %entry +; MIPS32-NEXT: srlv $1, $9, $3 +; MIPS32-NEXT: sll $1, $1, 24 +; MIPS32-NEXT: sra $1, $1, 24 +; MIPS32-NEXT: # %bb.4: # %entry +; MIPS32-NEXT: sll $1, $1, 24 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: sra $2, $1, 24 +; +; MIPS32O0-LABEL: AtomicCmpSwap8: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addiu $sp, $sp, -8 +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: lw $2, %got(y)($2) +; MIPS32O0-NEXT: addiu $25, $zero, -4 +; MIPS32O0-NEXT: and $25, $2, $25 +; MIPS32O0-NEXT: andi $2, $2, 3 +; MIPS32O0-NEXT: sll $2, $2, 3 +; MIPS32O0-NEXT: ori $1, $zero, 255 +; MIPS32O0-NEXT: sllv $1, $1, $2 +; MIPS32O0-NEXT: nor $3, $zero, $1 +; MIPS32O0-NEXT: andi $4, $4, 255 +; MIPS32O0-NEXT: sllv $4, $4, $2 +; MIPS32O0-NEXT: andi $5, $5, 255 +; MIPS32O0-NEXT: sllv $5, $5, $2 +; MIPS32O0-NEXT: $BB12_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $7, 0($25) +; MIPS32O0-NEXT: and $8, $7, $1 +; MIPS32O0-NEXT: bne $8, $4, $BB12_3 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: # in Loop: Header=BB12_1 Depth=1 +; MIPS32O0-NEXT: and $7, $7, $3 +; MIPS32O0-NEXT: or $7, $7, $5 +; MIPS32O0-NEXT: sc $7, 0($25) +; MIPS32O0-NEXT: beqz $7, $BB12_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: $BB12_3: # %entry +; MIPS32O0-NEXT: srlv $6, $8, $2 +; MIPS32O0-NEXT: sll $6, $6, 24 +; MIPS32O0-NEXT: sra $6, $6, 24 +; MIPS32O0-NEXT: # %bb.4: # %entry +; MIPS32O0-NEXT: sw $6, 4($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: # %bb.5: # %entry +; MIPS32O0-NEXT: lw $1, 4($sp) # 4-byte Folded Reload +; MIPS32O0-NEXT: sll $2, $1, 24 +; MIPS32O0-NEXT: sra $2, $2, 24 +; MIPS32O0-NEXT: addiu $sp, $sp, 8 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicCmpSwap8: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: lw $1, %got(y)($1) +; MIPS32R2-NEXT: addiu $2, $zero, -4 +; MIPS32R2-NEXT: and $3, $1, $2 +; MIPS32R2-NEXT: andi $1, $1, 3 +; MIPS32R2-NEXT: sll $1, $1, 3 +; MIPS32R2-NEXT: ori $2, $zero, 255 +; MIPS32R2-NEXT: sllv $6, $2, $1 +; MIPS32R2-NEXT: nor $7, $zero, $6 +; MIPS32R2-NEXT: andi $2, $4, 255 +; MIPS32R2-NEXT: sllv $4, $2, $1 +; MIPS32R2-NEXT: andi $2, $5, 255 +; MIPS32R2-NEXT: sllv $5, $2, $1 +; MIPS32R2-NEXT: $BB12_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $8, 0($3) +; MIPS32R2-NEXT: and $9, $8, $6 +; MIPS32R2-NEXT: bne $9, $4, $BB12_3 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: # in Loop: Header=BB12_1 Depth=1 +; MIPS32R2-NEXT: and $8, $8, $7 +; MIPS32R2-NEXT: or $8, $8, $5 +; MIPS32R2-NEXT: sc $8, 0($3) +; MIPS32R2-NEXT: beqz $8, $BB12_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: $BB12_3: # %entry +; MIPS32R2-NEXT: srlv $2, $9, $1 +; MIPS32R2-NEXT: seb $2, $2 +; MIPS32R2-NEXT: # %bb.4: # %entry +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: nop +; +; MIPS32R6-LABEL: AtomicCmpSwap8: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: lw $1, %got(y)($1) +; MIPS32R6-NEXT: addiu $2, $zero, -4 +; MIPS32R6-NEXT: and $3, $1, $2 +; MIPS32R6-NEXT: andi $1, $1, 3 +; MIPS32R6-NEXT: sll $1, $1, 3 +; MIPS32R6-NEXT: ori $2, $zero, 255 +; MIPS32R6-NEXT: sllv $6, $2, $1 +; MIPS32R6-NEXT: nor $7, $zero, $6 +; MIPS32R6-NEXT: andi $2, $4, 255 +; MIPS32R6-NEXT: sllv $4, $2, $1 +; MIPS32R6-NEXT: andi $2, $5, 255 +; MIPS32R6-NEXT: sllv $5, $2, $1 +; MIPS32R6-NEXT: $BB12_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $8, 0($3) +; MIPS32R6-NEXT: and $9, $8, $6 +; MIPS32R6-NEXT: bnec $9, $4, $BB12_3 +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: # in Loop: Header=BB12_1 Depth=1 +; MIPS32R6-NEXT: and $8, $8, $7 +; MIPS32R6-NEXT: or $8, $8, $5 +; MIPS32R6-NEXT: sc $8, 0($3) +; MIPS32R6-NEXT: beqzc $8, $BB12_1 +; MIPS32R6-NEXT: $BB12_3: # %entry +; MIPS32R6-NEXT: srlv $2, $9, $1 +; MIPS32R6-NEXT: seb $2, $2 +; MIPS32R6-NEXT: # %bb.4: # %entry +; MIPS32R6-NEXT: jrc $ra +; +; MIPS32R6O0-LABEL: AtomicCmpSwap8: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -16 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $5 +; MIPS32R6O0-NEXT: move $1, $4 +; MIPS32R6O0-NEXT: lw $2, %got(y)($2) +; MIPS32R6O0-NEXT: addiu $3, $zero, -4 +; MIPS32R6O0-NEXT: and $3, $2, $3 +; MIPS32R6O0-NEXT: andi $2, $2, 3 +; MIPS32R6O0-NEXT: sll $2, $2, 3 +; MIPS32R6O0-NEXT: ori $6, $zero, 255 +; MIPS32R6O0-NEXT: sllv $6, $6, $2 +; MIPS32R6O0-NEXT: nor $7, $zero, $6 +; MIPS32R6O0-NEXT: andi $4, $4, 255 +; MIPS32R6O0-NEXT: sllv $4, $4, $2 +; MIPS32R6O0-NEXT: andi $5, $5, 255 +; MIPS32R6O0-NEXT: sllv $5, $5, $2 +; MIPS32R6O0-NEXT: $BB12_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $9, 0($3) +; MIPS32R6O0-NEXT: and $10, $9, $6 +; MIPS32R6O0-NEXT: bnec $10, $4, $BB12_3 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: # in Loop: Header=BB12_1 Depth=1 +; MIPS32R6O0-NEXT: and $9, $9, $7 +; MIPS32R6O0-NEXT: or $9, $9, $5 +; MIPS32R6O0-NEXT: sc $9, 0($3) +; MIPS32R6O0-NEXT: beqzc $9, $BB12_1 +; MIPS32R6O0-NEXT: $BB12_3: # %entry +; MIPS32R6O0-NEXT: srlv $8, $10, $2 +; MIPS32R6O0-NEXT: seb $8, $8 +; MIPS32R6O0-NEXT: # %bb.4: # %entry +; MIPS32R6O0-NEXT: sw $1, 12($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $8, 8($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: # %bb.5: # %entry +; MIPS32R6O0-NEXT: lw $2, 8($sp) # 4-byte Folded Reload +; MIPS32R6O0-NEXT: addiu $sp, $sp, 16 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicCmpSwap8: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap8))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap8))) +; MIPS4-NEXT: ld $1, %got_disp(y)($1) +; MIPS4-NEXT: daddiu $2, $zero, -4 +; MIPS4-NEXT: and $2, $1, $2 +; MIPS4-NEXT: andi $1, $1, 3 +; MIPS4-NEXT: sll $3, $1, 3 +; MIPS4-NEXT: ori $1, $zero, 255 +; MIPS4-NEXT: sllv $6, $1, $3 +; MIPS4-NEXT: nor $7, $zero, $6 +; MIPS4-NEXT: andi $1, $4, 255 +; MIPS4-NEXT: sllv $4, $1, $3 +; MIPS4-NEXT: andi $1, $5, 255 +; MIPS4-NEXT: sllv $5, $1, $3 +; MIPS4-NEXT: .LBB12_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $8, 0($2) +; MIPS4-NEXT: and $9, $8, $6 +; MIPS4-NEXT: bne $9, $4, .LBB12_3 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: # in Loop: Header=BB12_1 Depth=1 +; MIPS4-NEXT: and $8, $8, $7 +; MIPS4-NEXT: or $8, $8, $5 +; MIPS4-NEXT: sc $8, 0($2) +; MIPS4-NEXT: beqz $8, .LBB12_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: .LBB12_3: # %entry +; MIPS4-NEXT: srlv $1, $9, $3 +; MIPS4-NEXT: sll $1, $1, 24 +; MIPS4-NEXT: sra $1, $1, 24 +; MIPS4-NEXT: # %bb.4: # %entry +; MIPS4-NEXT: sll $1, $1, 24 +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: sra $2, $1, 24 +; +; MIPS64-LABEL: AtomicCmpSwap8: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap8))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap8))) +; MIPS64-NEXT: ld $1, %got_disp(y)($1) +; MIPS64-NEXT: daddiu $2, $zero, -4 +; MIPS64-NEXT: and $2, $1, $2 +; MIPS64-NEXT: andi $1, $1, 3 +; MIPS64-NEXT: sll $3, $1, 3 +; MIPS64-NEXT: ori $1, $zero, 255 +; MIPS64-NEXT: sllv $6, $1, $3 +; MIPS64-NEXT: nor $7, $zero, $6 +; MIPS64-NEXT: andi $1, $4, 255 +; MIPS64-NEXT: sllv $4, $1, $3 +; MIPS64-NEXT: andi $1, $5, 255 +; MIPS64-NEXT: sllv $5, $1, $3 +; MIPS64-NEXT: .LBB12_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $8, 0($2) +; MIPS64-NEXT: and $9, $8, $6 +; MIPS64-NEXT: bne $9, $4, .LBB12_3 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: # in Loop: Header=BB12_1 Depth=1 +; MIPS64-NEXT: and $8, $8, $7 +; MIPS64-NEXT: or $8, $8, $5 +; MIPS64-NEXT: sc $8, 0($2) +; MIPS64-NEXT: beqz $8, .LBB12_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: .LBB12_3: # %entry +; MIPS64-NEXT: srlv $1, $9, $3 +; MIPS64-NEXT: sll $1, $1, 24 +; MIPS64-NEXT: sra $1, $1, 24 +; MIPS64-NEXT: # %bb.4: # %entry +; MIPS64-NEXT: sll $1, $1, 24 +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: sra $2, $1, 24 +; +; MIPS64R2-LABEL: AtomicCmpSwap8: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap8))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap8))) +; MIPS64R2-NEXT: ld $1, %got_disp(y)($1) +; MIPS64R2-NEXT: daddiu $2, $zero, -4 +; MIPS64R2-NEXT: and $3, $1, $2 +; MIPS64R2-NEXT: andi $1, $1, 3 +; MIPS64R2-NEXT: sll $1, $1, 3 +; MIPS64R2-NEXT: ori $2, $zero, 255 +; MIPS64R2-NEXT: sllv $6, $2, $1 +; MIPS64R2-NEXT: nor $7, $zero, $6 +; MIPS64R2-NEXT: andi $2, $4, 255 +; MIPS64R2-NEXT: sllv $4, $2, $1 +; MIPS64R2-NEXT: andi $2, $5, 255 +; MIPS64R2-NEXT: sllv $5, $2, $1 +; MIPS64R2-NEXT: .LBB12_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $8, 0($3) +; MIPS64R2-NEXT: and $9, $8, $6 +; MIPS64R2-NEXT: bne $9, $4, .LBB12_3 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: # in Loop: Header=BB12_1 Depth=1 +; MIPS64R2-NEXT: and $8, $8, $7 +; MIPS64R2-NEXT: or $8, $8, $5 +; MIPS64R2-NEXT: sc $8, 0($3) +; MIPS64R2-NEXT: beqz $8, .LBB12_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: .LBB12_3: # %entry +; MIPS64R2-NEXT: srlv $2, $9, $1 +; MIPS64R2-NEXT: seb $2, $2 +; MIPS64R2-NEXT: # %bb.4: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: AtomicCmpSwap8: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap8))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap8))) +; MIPS64R6-NEXT: ld $1, %got_disp(y)($1) +; MIPS64R6-NEXT: daddiu $2, $zero, -4 +; MIPS64R6-NEXT: and $3, $1, $2 +; MIPS64R6-NEXT: andi $1, $1, 3 +; MIPS64R6-NEXT: sll $1, $1, 3 +; MIPS64R6-NEXT: ori $2, $zero, 255 +; MIPS64R6-NEXT: sllv $6, $2, $1 +; MIPS64R6-NEXT: nor $7, $zero, $6 +; MIPS64R6-NEXT: andi $2, $4, 255 +; MIPS64R6-NEXT: sllv $4, $2, $1 +; MIPS64R6-NEXT: andi $2, $5, 255 +; MIPS64R6-NEXT: sllv $5, $2, $1 +; MIPS64R6-NEXT: .LBB12_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $8, 0($3) +; MIPS64R6-NEXT: and $9, $8, $6 +; MIPS64R6-NEXT: bnec $9, $4, .LBB12_3 +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: # in Loop: Header=BB12_1 Depth=1 +; MIPS64R6-NEXT: and $8, $8, $7 +; MIPS64R6-NEXT: or $8, $8, $5 +; MIPS64R6-NEXT: sc $8, 0($3) +; MIPS64R6-NEXT: beqzc $8, .LBB12_1 +; MIPS64R6-NEXT: .LBB12_3: # %entry +; MIPS64R6-NEXT: srlv $2, $9, $1 +; MIPS64R6-NEXT: seb $2, $2 +; MIPS64R6-NEXT: # %bb.4: # %entry +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: AtomicCmpSwap8: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap8))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap8))) +; MIPS64R6O0-NEXT: move $2, $5 +; MIPS64R6O0-NEXT: move $3, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(y)($1) +; MIPS64R6O0-NEXT: daddiu $4, $zero, -4 +; MIPS64R6O0-NEXT: and $4, $1, $4 +; MIPS64R6O0-NEXT: andi $6, $1, 3 +; MIPS64R6O0-NEXT: xori $6, $6, 3 +; MIPS64R6O0-NEXT: sll $6, $6, 3 +; MIPS64R6O0-NEXT: ori $7, $zero, 255 +; MIPS64R6O0-NEXT: sllv $7, $7, $6 +; MIPS64R6O0-NEXT: nor $8, $zero, $7 +; MIPS64R6O0-NEXT: andi $3, $3, 255 +; MIPS64R6O0-NEXT: sllv $3, $3, $6 +; MIPS64R6O0-NEXT: andi $2, $2, 255 +; MIPS64R6O0-NEXT: sllv $2, $2, $6 +; MIPS64R6O0-NEXT: .LBB12_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $10, 0($4) +; MIPS64R6O0-NEXT: and $11, $10, $7 +; MIPS64R6O0-NEXT: bnec $11, $3, .LBB12_3 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: # in Loop: Header=BB12_1 Depth=1 +; MIPS64R6O0-NEXT: and $10, $10, $8 +; MIPS64R6O0-NEXT: or $10, $10, $2 +; MIPS64R6O0-NEXT: sc $10, 0($4) +; MIPS64R6O0-NEXT: beqzc $10, .LBB12_1 +; MIPS64R6O0-NEXT: .LBB12_3: # %entry +; MIPS64R6O0-NEXT: srlv $9, $11, $6 +; MIPS64R6O0-NEXT: seb $9, $9 +; MIPS64R6O0-NEXT: # %bb.4: # %entry +; MIPS64R6O0-NEXT: sw $9, 12($sp) # 4-byte Folded Spill +; MIPS64R6O0-NEXT: # %bb.5: # %entry +; MIPS64R6O0-NEXT: lw $2, 12($sp) # 4-byte Folded Reload +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicCmpSwap8: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: lw $1, %got(y)($2) +; MM32-NEXT: addiu $2, $zero, -4 +; MM32-NEXT: and $3, $1, $2 +; MM32-NEXT: andi $1, $1, 3 +; MM32-NEXT: sll $1, $1, 3 +; MM32-NEXT: ori $2, $zero, 255 +; MM32-NEXT: sllv $6, $2, $1 +; MM32-NEXT: nor $7, $zero, $6 +; MM32-NEXT: andi $2, $4, 255 +; MM32-NEXT: sllv $4, $2, $1 +; MM32-NEXT: andi $2, $5, 255 +; MM32-NEXT: sllv $5, $2, $1 +; MM32-NEXT: $BB12_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $8, 0($3) +; MM32-NEXT: and $9, $8, $6 +; MM32-NEXT: bne $9, $4, $BB12_3 +; MM32-NEXT: nop +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: # in Loop: Header=BB12_1 Depth=1 +; MM32-NEXT: and $8, $8, $7 +; MM32-NEXT: or $8, $8, $5 +; MM32-NEXT: sc $8, 0($3) +; MM32-NEXT: beqzc $8, $BB12_1 +; MM32-NEXT: $BB12_3: # %entry +; MM32-NEXT: srlv $2, $9, $1 +; MM32-NEXT: seb $2, $2 +; MM32-NEXT: # %bb.4: # %entry +; MM32-NEXT: jrc $ra +; +; O1-LABEL: AtomicCmpSwap8: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: lw $1, %got(y)($1) +; O1-NEXT: addiu $2, $zero, -4 +; O1-NEXT: and $2, $1, $2 +; O1-NEXT: andi $1, $1, 3 +; O1-NEXT: sll $3, $1, 3 +; O1-NEXT: ori $1, $zero, 255 +; O1-NEXT: sllv $6, $1, $3 +; O1-NEXT: nor $7, $zero, $6 +; O1-NEXT: andi $1, $4, 255 +; O1-NEXT: sllv $4, $1, $3 +; O1-NEXT: andi $1, $5, 255 +; O1-NEXT: sllv $5, $1, $3 +; O1-NEXT: $BB12_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $8, 0($2) +; O1-NEXT: and $9, $8, $6 +; O1-NEXT: bne $9, $4, $BB12_3 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: # in Loop: Header=BB12_1 Depth=1 +; O1-NEXT: and $8, $8, $7 +; O1-NEXT: or $8, $8, $5 +; O1-NEXT: sc $8, 0($2) +; O1-NEXT: beqz $8, $BB12_1 +; O1-NEXT: nop +; O1-NEXT: $BB12_3: # %entry +; O1-NEXT: srlv $1, $9, $3 +; O1-NEXT: sll $1, $1, 24 +; O1-NEXT: sra $1, $1, 24 +; O1-NEXT: # %bb.4: # %entry +; O1-NEXT: sll $1, $1, 24 +; O1-NEXT: jr $ra +; O1-NEXT: sra $2, $1, 24 +; +; O2-LABEL: AtomicCmpSwap8: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: lw $1, %got(y)($1) +; O2-NEXT: addiu $2, $zero, -4 +; O2-NEXT: and $2, $1, $2 +; O2-NEXT: andi $1, $1, 3 +; O2-NEXT: sll $3, $1, 3 +; O2-NEXT: ori $1, $zero, 255 +; O2-NEXT: sllv $6, $1, $3 +; O2-NEXT: nor $7, $zero, $6 +; O2-NEXT: andi $1, $4, 255 +; O2-NEXT: sllv $4, $1, $3 +; O2-NEXT: andi $1, $5, 255 +; O2-NEXT: sllv $5, $1, $3 +; O2-NEXT: $BB12_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $8, 0($2) +; O2-NEXT: and $9, $8, $6 +; O2-NEXT: bne $9, $4, $BB12_3 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: # in Loop: Header=BB12_1 Depth=1 +; O2-NEXT: and $8, $8, $7 +; O2-NEXT: or $8, $8, $5 +; O2-NEXT: sc $8, 0($2) +; O2-NEXT: beqz $8, $BB12_1 +; O2-NEXT: nop +; O2-NEXT: $BB12_3: # %entry +; O2-NEXT: srlv $1, $9, $3 +; O2-NEXT: sll $1, $1, 24 +; O2-NEXT: sra $1, $1, 24 +; O2-NEXT: # %bb.4: # %entry +; O2-NEXT: sll $1, $1, 24 +; O2-NEXT: jr $ra +; O2-NEXT: sra $2, $1, 24 +; +; O3-LABEL: AtomicCmpSwap8: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: addiu $2, $zero, -4 +; O3-NEXT: lw $1, %got(y)($1) +; O3-NEXT: and $2, $1, $2 +; O3-NEXT: andi $1, $1, 3 +; O3-NEXT: sll $3, $1, 3 +; O3-NEXT: ori $1, $zero, 255 +; O3-NEXT: sllv $6, $1, $3 +; O3-NEXT: andi $1, $4, 255 +; O3-NEXT: sllv $4, $1, $3 +; O3-NEXT: andi $1, $5, 255 +; O3-NEXT: nor $7, $zero, $6 +; O3-NEXT: sllv $5, $1, $3 +; O3-NEXT: $BB12_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $8, 0($2) +; O3-NEXT: and $9, $8, $6 +; O3-NEXT: bne $9, $4, $BB12_3 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: # in Loop: Header=BB12_1 Depth=1 +; O3-NEXT: and $8, $8, $7 +; O3-NEXT: or $8, $8, $5 +; O3-NEXT: sc $8, 0($2) +; O3-NEXT: beqz $8, $BB12_1 +; O3-NEXT: nop +; O3-NEXT: $BB12_3: # %entry +; O3-NEXT: srlv $1, $9, $3 +; O3-NEXT: sll $1, $1, 24 +; O3-NEXT: sra $1, $1, 24 +; O3-NEXT: # %bb.4: # %entry +; O3-NEXT: sll $1, $1, 24 +; O3-NEXT: jr $ra +; O3-NEXT: sra $2, $1, 24 +; +; MIPS32EB-LABEL: AtomicCmpSwap8: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: lw $1, %got(y)($1) +; MIPS32EB-NEXT: addiu $2, $zero, -4 +; MIPS32EB-NEXT: and $2, $1, $2 +; MIPS32EB-NEXT: andi $1, $1, 3 +; MIPS32EB-NEXT: xori $1, $1, 3 +; MIPS32EB-NEXT: sll $3, $1, 3 +; MIPS32EB-NEXT: ori $1, $zero, 255 +; MIPS32EB-NEXT: sllv $6, $1, $3 +; MIPS32EB-NEXT: nor $7, $zero, $6 +; MIPS32EB-NEXT: andi $1, $4, 255 +; MIPS32EB-NEXT: sllv $4, $1, $3 +; MIPS32EB-NEXT: andi $1, $5, 255 +; MIPS32EB-NEXT: sllv $5, $1, $3 +; MIPS32EB-NEXT: $BB12_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $8, 0($2) +; MIPS32EB-NEXT: and $9, $8, $6 +; MIPS32EB-NEXT: bne $9, $4, $BB12_3 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: # in Loop: Header=BB12_1 Depth=1 +; MIPS32EB-NEXT: and $8, $8, $7 +; MIPS32EB-NEXT: or $8, $8, $5 +; MIPS32EB-NEXT: sc $8, 0($2) +; MIPS32EB-NEXT: beqz $8, $BB12_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: $BB12_3: # %entry +; MIPS32EB-NEXT: srlv $1, $9, $3 +; MIPS32EB-NEXT: sll $1, $1, 24 +; MIPS32EB-NEXT: sra $1, $1, 24 +; MIPS32EB-NEXT: # %bb.4: # %entry +; MIPS32EB-NEXT: sll $1, $1, 24 +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: sra $2, $1, 24 entry: %pair0 = cmpxchg i8* @y, i8 %oldval, i8 %newval monotonic monotonic %0 = extractvalue { i8, i1 } %pair0, 0 ret i8 %0 - -; ALL-LABEL: AtomicCmpSwap8: - -; MIPS32-ANY: lw $[[R0:[0-9]+]], %got(y) -; MIPS64-ANY: ld $[[R0:[0-9]+]], %got_disp(y)( - -; ALL: addiu $[[R1:[0-9]+]], $zero, -4 -; ALL: and $[[R2:[0-9]+]], $[[R0]], $[[R1]] -; ALL: andi $[[R3:[0-9]+]], $[[R0]], 3 -; CHECK-EL: sll $[[R5:[0-9]+]], $[[R3]], 3 -; CHECK-EB: xori $[[R4:[0-9]+]], $[[R3]], 3 -; CHECK-EB: sll $[[R5:[0-9]+]], $[[R4]], 3 -; ALL: ori $[[R6:[0-9]+]], $zero, 255 -; ALL: sllv $[[R7:[0-9]+]], $[[R6]], $[[R5]] -; ALL: nor $[[R8:[0-9]+]], $zero, $[[R7]] -; ALL: andi $[[R9:[0-9]+]], $4, 255 -; ALL: sllv $[[R10:[0-9]+]], $[[R9]], $[[R5]] -; ALL: andi $[[R11:[0-9]+]], $5, 255 -; ALL: sllv $[[R12:[0-9]+]], $[[R11]], $[[R5]] - -; ALL: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; ALL: ll $[[R13:[0-9]+]], 0($[[R2]]) -; ALL: and $[[R14:[0-9]+]], $[[R13]], $[[R7]] -; NOT-MICROMIPS: bne $[[R14]], $[[R10]], [[BB1:(\$|\.L)[A-Z_0-9]+]] -; MICROMIPS: bne $[[R14]], $[[R10]], [[BB1:(\$|\.L)[A-Z_0-9]+]] -; MIPSR6: bnec $[[R14]], $[[R10]], [[BB1:(\$|\.L)[A-Z_0-9]+]] - -; ALL: and $[[R15:[0-9]+]], $[[R13]], $[[R8]] -; ALL: or $[[R16:[0-9]+]], $[[R15]], $[[R12]] -; ALL: sc $[[R16]], 0($[[R2]]) -; NOT-MICROMIPS: beqz $[[R16]], [[BB0]] -; MICROMIPS: beqzc $[[R16]], [[BB0]] -; MIPSR6: beqzc $[[R16]], [[BB0]] - -; ALL: [[BB1]]: -; ALL: srlv $[[R17:[0-9]+]], $[[R14]], $[[R5]] - -; NO-SEB-SEH: sll $[[R18:[0-9]+]], $[[R17]], 24 -; NO-SEB-SEH: sra $2, $[[R18]], 24 - -; HAS-SEB-SEH: seb $2, $[[R17]] } define i1 @AtomicCmpSwapRes8(i8* %ptr, i8 signext %oldval, i8 signext %newval) nounwind { +; MIPS32-LABEL: AtomicCmpSwapRes8: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: addiu $1, $zero, -4 +; MIPS32-NEXT: and $2, $4, $1 +; MIPS32-NEXT: andi $1, $4, 3 +; MIPS32-NEXT: sll $3, $1, 3 +; MIPS32-NEXT: ori $1, $zero, 255 +; MIPS32-NEXT: sllv $4, $1, $3 +; MIPS32-NEXT: nor $7, $zero, $4 +; MIPS32-NEXT: andi $1, $5, 255 +; MIPS32-NEXT: sllv $8, $1, $3 +; MIPS32-NEXT: andi $1, $6, 255 +; MIPS32-NEXT: sllv $6, $1, $3 +; MIPS32-NEXT: $BB13_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $9, 0($2) +; MIPS32-NEXT: and $10, $9, $4 +; MIPS32-NEXT: bne $10, $8, $BB13_3 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: # in Loop: Header=BB13_1 Depth=1 +; MIPS32-NEXT: and $9, $9, $7 +; MIPS32-NEXT: or $9, $9, $6 +; MIPS32-NEXT: sc $9, 0($2) +; MIPS32-NEXT: beqz $9, $BB13_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: $BB13_3: # %entry +; MIPS32-NEXT: srlv $1, $10, $3 +; MIPS32-NEXT: sll $1, $1, 24 +; MIPS32-NEXT: sra $1, $1, 24 +; MIPS32-NEXT: # %bb.4: # %entry +; MIPS32-NEXT: sll $2, $5, 24 +; MIPS32-NEXT: sra $2, $2, 24 +; MIPS32-NEXT: xor $1, $1, $2 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: sltiu $2, $1, 1 +; +; MIPS32O0-LABEL: AtomicCmpSwapRes8: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: addiu $sp, $sp, -8 +; MIPS32O0-NEXT: addiu $1, $zero, -4 +; MIPS32O0-NEXT: and $1, $4, $1 +; MIPS32O0-NEXT: andi $4, $4, 3 +; MIPS32O0-NEXT: sll $4, $4, 3 +; MIPS32O0-NEXT: ori $2, $zero, 255 +; MIPS32O0-NEXT: sllv $2, $2, $4 +; MIPS32O0-NEXT: nor $3, $zero, $2 +; MIPS32O0-NEXT: andi $7, $5, 255 +; MIPS32O0-NEXT: sllv $7, $7, $4 +; MIPS32O0-NEXT: andi $6, $6, 255 +; MIPS32O0-NEXT: sllv $6, $6, $4 +; MIPS32O0-NEXT: $BB13_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $9, 0($1) +; MIPS32O0-NEXT: and $10, $9, $2 +; MIPS32O0-NEXT: bne $10, $7, $BB13_3 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: # in Loop: Header=BB13_1 Depth=1 +; MIPS32O0-NEXT: and $9, $9, $3 +; MIPS32O0-NEXT: or $9, $9, $6 +; MIPS32O0-NEXT: sc $9, 0($1) +; MIPS32O0-NEXT: beqz $9, $BB13_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: $BB13_3: # %entry +; MIPS32O0-NEXT: srlv $8, $10, $4 +; MIPS32O0-NEXT: sll $8, $8, 24 +; MIPS32O0-NEXT: sra $8, $8, 24 +; MIPS32O0-NEXT: # %bb.4: # %entry +; MIPS32O0-NEXT: sw $5, 4($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: sw $8, 0($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: # %bb.5: # %entry +; MIPS32O0-NEXT: lw $1, 4($sp) # 4-byte Folded Reload +; MIPS32O0-NEXT: sll $2, $1, 24 +; MIPS32O0-NEXT: sra $2, $2, 24 +; MIPS32O0-NEXT: lw $3, 0($sp) # 4-byte Folded Reload +; MIPS32O0-NEXT: xor $2, $3, $2 +; MIPS32O0-NEXT: sltiu $2, $2, 1 +; MIPS32O0-NEXT: addiu $sp, $sp, 8 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicCmpSwapRes8: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: addiu $1, $zero, -4 +; MIPS32R2-NEXT: and $2, $4, $1 +; MIPS32R2-NEXT: andi $1, $4, 3 +; MIPS32R2-NEXT: sll $3, $1, 3 +; MIPS32R2-NEXT: ori $1, $zero, 255 +; MIPS32R2-NEXT: sllv $4, $1, $3 +; MIPS32R2-NEXT: nor $7, $zero, $4 +; MIPS32R2-NEXT: andi $1, $5, 255 +; MIPS32R2-NEXT: sllv $8, $1, $3 +; MIPS32R2-NEXT: andi $1, $6, 255 +; MIPS32R2-NEXT: sllv $6, $1, $3 +; MIPS32R2-NEXT: $BB13_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $9, 0($2) +; MIPS32R2-NEXT: and $10, $9, $4 +; MIPS32R2-NEXT: bne $10, $8, $BB13_3 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: # in Loop: Header=BB13_1 Depth=1 +; MIPS32R2-NEXT: and $9, $9, $7 +; MIPS32R2-NEXT: or $9, $9, $6 +; MIPS32R2-NEXT: sc $9, 0($2) +; MIPS32R2-NEXT: beqz $9, $BB13_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: $BB13_3: # %entry +; MIPS32R2-NEXT: srlv $1, $10, $3 +; MIPS32R2-NEXT: seb $1, $1 +; MIPS32R2-NEXT: # %bb.4: # %entry +; MIPS32R2-NEXT: xor $1, $1, $5 +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: sltiu $2, $1, 1 +; +; MIPS32R6-LABEL: AtomicCmpSwapRes8: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: addiu $1, $zero, -4 +; MIPS32R6-NEXT: and $2, $4, $1 +; MIPS32R6-NEXT: andi $1, $4, 3 +; MIPS32R6-NEXT: sll $3, $1, 3 +; MIPS32R6-NEXT: ori $1, $zero, 255 +; MIPS32R6-NEXT: sllv $4, $1, $3 +; MIPS32R6-NEXT: nor $7, $zero, $4 +; MIPS32R6-NEXT: andi $1, $5, 255 +; MIPS32R6-NEXT: sllv $8, $1, $3 +; MIPS32R6-NEXT: andi $1, $6, 255 +; MIPS32R6-NEXT: sllv $6, $1, $3 +; MIPS32R6-NEXT: $BB13_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $9, 0($2) +; MIPS32R6-NEXT: and $10, $9, $4 +; MIPS32R6-NEXT: bnec $10, $8, $BB13_3 +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: # in Loop: Header=BB13_1 Depth=1 +; MIPS32R6-NEXT: and $9, $9, $7 +; MIPS32R6-NEXT: or $9, $9, $6 +; MIPS32R6-NEXT: sc $9, 0($2) +; MIPS32R6-NEXT: beqzc $9, $BB13_1 +; MIPS32R6-NEXT: $BB13_3: # %entry +; MIPS32R6-NEXT: srlv $1, $10, $3 +; MIPS32R6-NEXT: seb $1, $1 +; MIPS32R6-NEXT: # %bb.4: # %entry +; MIPS32R6-NEXT: xor $1, $1, $5 +; MIPS32R6-NEXT: jr $ra +; MIPS32R6-NEXT: sltiu $2, $1, 1 +; +; MIPS32R6O0-LABEL: AtomicCmpSwapRes8: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: addiu $sp, $sp, -24 +; MIPS32R6O0-NEXT: move $1, $6 +; MIPS32R6O0-NEXT: move $2, $5 +; MIPS32R6O0-NEXT: move $3, $4 +; MIPS32R6O0-NEXT: addiu $7, $zero, -4 +; MIPS32R6O0-NEXT: and $7, $4, $7 +; MIPS32R6O0-NEXT: andi $4, $4, 3 +; MIPS32R6O0-NEXT: sll $4, $4, 3 +; MIPS32R6O0-NEXT: ori $8, $zero, 255 +; MIPS32R6O0-NEXT: sllv $8, $8, $4 +; MIPS32R6O0-NEXT: nor $9, $zero, $8 +; MIPS32R6O0-NEXT: andi $10, $5, 255 +; MIPS32R6O0-NEXT: sllv $10, $10, $4 +; MIPS32R6O0-NEXT: andi $6, $6, 255 +; MIPS32R6O0-NEXT: sllv $6, $6, $4 +; MIPS32R6O0-NEXT: $BB13_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $12, 0($7) +; MIPS32R6O0-NEXT: and $13, $12, $8 +; MIPS32R6O0-NEXT: bnec $13, $10, $BB13_3 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: # in Loop: Header=BB13_1 Depth=1 +; MIPS32R6O0-NEXT: and $12, $12, $9 +; MIPS32R6O0-NEXT: or $12, $12, $6 +; MIPS32R6O0-NEXT: sc $12, 0($7) +; MIPS32R6O0-NEXT: beqzc $12, $BB13_1 +; MIPS32R6O0-NEXT: $BB13_3: # %entry +; MIPS32R6O0-NEXT: srlv $11, $13, $4 +; MIPS32R6O0-NEXT: seb $11, $11 +; MIPS32R6O0-NEXT: # %bb.4: # %entry +; MIPS32R6O0-NEXT: sw $11, 20($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $5, 16($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $3, 12($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $1, 8($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $2, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: # %bb.5: # %entry +; MIPS32R6O0-NEXT: lw $1, 20($sp) # 4-byte Folded Reload +; MIPS32R6O0-NEXT: lw $2, 16($sp) # 4-byte Folded Reload +; MIPS32R6O0-NEXT: xor $1, $1, $2 +; MIPS32R6O0-NEXT: sltiu $2, $1, 1 +; MIPS32R6O0-NEXT: addiu $sp, $sp, 24 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicCmpSwapRes8: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: daddiu $1, $zero, -4 +; MIPS4-NEXT: and $2, $4, $1 +; MIPS4-NEXT: andi $1, $4, 3 +; MIPS4-NEXT: sll $3, $1, 3 +; MIPS4-NEXT: ori $1, $zero, 255 +; MIPS4-NEXT: sllv $4, $1, $3 +; MIPS4-NEXT: nor $7, $zero, $4 +; MIPS4-NEXT: andi $1, $5, 255 +; MIPS4-NEXT: sllv $8, $1, $3 +; MIPS4-NEXT: andi $1, $6, 255 +; MIPS4-NEXT: sllv $6, $1, $3 +; MIPS4-NEXT: .LBB13_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $9, 0($2) +; MIPS4-NEXT: and $10, $9, $4 +; MIPS4-NEXT: bne $10, $8, .LBB13_3 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: # in Loop: Header=BB13_1 Depth=1 +; MIPS4-NEXT: and $9, $9, $7 +; MIPS4-NEXT: or $9, $9, $6 +; MIPS4-NEXT: sc $9, 0($2) +; MIPS4-NEXT: beqz $9, .LBB13_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: .LBB13_3: # %entry +; MIPS4-NEXT: srlv $1, $10, $3 +; MIPS4-NEXT: sll $1, $1, 24 +; MIPS4-NEXT: sra $1, $1, 24 +; MIPS4-NEXT: # %bb.4: # %entry +; MIPS4-NEXT: sll $2, $5, 24 +; MIPS4-NEXT: sra $2, $2, 24 +; MIPS4-NEXT: xor $1, $1, $2 +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: sltiu $2, $1, 1 +; +; MIPS64-LABEL: AtomicCmpSwapRes8: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: daddiu $1, $zero, -4 +; MIPS64-NEXT: and $2, $4, $1 +; MIPS64-NEXT: andi $1, $4, 3 +; MIPS64-NEXT: sll $3, $1, 3 +; MIPS64-NEXT: ori $1, $zero, 255 +; MIPS64-NEXT: sllv $4, $1, $3 +; MIPS64-NEXT: nor $7, $zero, $4 +; MIPS64-NEXT: andi $1, $5, 255 +; MIPS64-NEXT: sllv $8, $1, $3 +; MIPS64-NEXT: andi $1, $6, 255 +; MIPS64-NEXT: sllv $6, $1, $3 +; MIPS64-NEXT: .LBB13_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $9, 0($2) +; MIPS64-NEXT: and $10, $9, $4 +; MIPS64-NEXT: bne $10, $8, .LBB13_3 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: # in Loop: Header=BB13_1 Depth=1 +; MIPS64-NEXT: and $9, $9, $7 +; MIPS64-NEXT: or $9, $9, $6 +; MIPS64-NEXT: sc $9, 0($2) +; MIPS64-NEXT: beqz $9, .LBB13_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: .LBB13_3: # %entry +; MIPS64-NEXT: srlv $1, $10, $3 +; MIPS64-NEXT: sll $1, $1, 24 +; MIPS64-NEXT: sra $1, $1, 24 +; MIPS64-NEXT: # %bb.4: # %entry +; MIPS64-NEXT: sll $2, $5, 24 +; MIPS64-NEXT: sra $2, $2, 24 +; MIPS64-NEXT: xor $1, $1, $2 +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: sltiu $2, $1, 1 +; +; MIPS64R2-LABEL: AtomicCmpSwapRes8: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: daddiu $1, $zero, -4 +; MIPS64R2-NEXT: and $2, $4, $1 +; MIPS64R2-NEXT: andi $1, $4, 3 +; MIPS64R2-NEXT: sll $3, $1, 3 +; MIPS64R2-NEXT: ori $1, $zero, 255 +; MIPS64R2-NEXT: sllv $4, $1, $3 +; MIPS64R2-NEXT: nor $7, $zero, $4 +; MIPS64R2-NEXT: andi $1, $5, 255 +; MIPS64R2-NEXT: sllv $8, $1, $3 +; MIPS64R2-NEXT: andi $1, $6, 255 +; MIPS64R2-NEXT: sllv $6, $1, $3 +; MIPS64R2-NEXT: .LBB13_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $9, 0($2) +; MIPS64R2-NEXT: and $10, $9, $4 +; MIPS64R2-NEXT: bne $10, $8, .LBB13_3 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: # in Loop: Header=BB13_1 Depth=1 +; MIPS64R2-NEXT: and $9, $9, $7 +; MIPS64R2-NEXT: or $9, $9, $6 +; MIPS64R2-NEXT: sc $9, 0($2) +; MIPS64R2-NEXT: beqz $9, .LBB13_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: .LBB13_3: # %entry +; MIPS64R2-NEXT: srlv $1, $10, $3 +; MIPS64R2-NEXT: seb $1, $1 +; MIPS64R2-NEXT: # %bb.4: # %entry +; MIPS64R2-NEXT: xor $1, $1, $5 +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: sltiu $2, $1, 1 +; +; MIPS64R6-LABEL: AtomicCmpSwapRes8: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: daddiu $1, $zero, -4 +; MIPS64R6-NEXT: and $2, $4, $1 +; MIPS64R6-NEXT: andi $1, $4, 3 +; MIPS64R6-NEXT: sll $3, $1, 3 +; MIPS64R6-NEXT: ori $1, $zero, 255 +; MIPS64R6-NEXT: sllv $4, $1, $3 +; MIPS64R6-NEXT: nor $7, $zero, $4 +; MIPS64R6-NEXT: andi $1, $5, 255 +; MIPS64R6-NEXT: sllv $8, $1, $3 +; MIPS64R6-NEXT: andi $1, $6, 255 +; MIPS64R6-NEXT: sllv $6, $1, $3 +; MIPS64R6-NEXT: .LBB13_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $9, 0($2) +; MIPS64R6-NEXT: and $10, $9, $4 +; MIPS64R6-NEXT: bnec $10, $8, .LBB13_3 +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: # in Loop: Header=BB13_1 Depth=1 +; MIPS64R6-NEXT: and $9, $9, $7 +; MIPS64R6-NEXT: or $9, $9, $6 +; MIPS64R6-NEXT: sc $9, 0($2) +; MIPS64R6-NEXT: beqzc $9, .LBB13_1 +; MIPS64R6-NEXT: .LBB13_3: # %entry +; MIPS64R6-NEXT: srlv $1, $10, $3 +; MIPS64R6-NEXT: seb $1, $1 +; MIPS64R6-NEXT: # %bb.4: # %entry +; MIPS64R6-NEXT: xor $1, $1, $5 +; MIPS64R6-NEXT: jr $ra +; MIPS64R6-NEXT: sltiu $2, $1, 1 +; +; MIPS64R6O0-LABEL: AtomicCmpSwapRes8: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: move $1, $6 +; MIPS64R6O0-NEXT: move $2, $5 +; MIPS64R6O0-NEXT: move $5, $4 +; MIPS64R6O0-NEXT: daddiu $6, $zero, -4 +; MIPS64R6O0-NEXT: and $6, $4, $6 +; MIPS64R6O0-NEXT: andi $3, $4, 3 +; MIPS64R6O0-NEXT: xori $3, $3, 3 +; MIPS64R6O0-NEXT: sll $3, $3, 3 +; MIPS64R6O0-NEXT: ori $7, $zero, 255 +; MIPS64R6O0-NEXT: sllv $7, $7, $3 +; MIPS64R6O0-NEXT: nor $8, $zero, $7 +; MIPS64R6O0-NEXT: andi $9, $2, 255 +; MIPS64R6O0-NEXT: sllv $9, $9, $3 +; MIPS64R6O0-NEXT: andi $1, $1, 255 +; MIPS64R6O0-NEXT: sllv $1, $1, $3 +; MIPS64R6O0-NEXT: .LBB13_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $11, 0($6) +; MIPS64R6O0-NEXT: and $12, $11, $7 +; MIPS64R6O0-NEXT: bnec $12, $9, .LBB13_3 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: # in Loop: Header=BB13_1 Depth=1 +; MIPS64R6O0-NEXT: and $11, $11, $8 +; MIPS64R6O0-NEXT: or $11, $11, $1 +; MIPS64R6O0-NEXT: sc $11, 0($6) +; MIPS64R6O0-NEXT: beqzc $11, .LBB13_1 +; MIPS64R6O0-NEXT: .LBB13_3: # %entry +; MIPS64R6O0-NEXT: srlv $10, $12, $3 +; MIPS64R6O0-NEXT: seb $10, $10 +; MIPS64R6O0-NEXT: # %bb.4: # %entry +; MIPS64R6O0-NEXT: sd $5, 8($sp) # 8-byte Folded Spill +; MIPS64R6O0-NEXT: sw $10, 4($sp) # 4-byte Folded Spill +; MIPS64R6O0-NEXT: sw $2, 0($sp) # 4-byte Folded Spill +; MIPS64R6O0-NEXT: # %bb.5: # %entry +; MIPS64R6O0-NEXT: lw $1, 4($sp) # 4-byte Folded Reload +; MIPS64R6O0-NEXT: lw $2, 0($sp) # 4-byte Folded Reload +; MIPS64R6O0-NEXT: xor $1, $1, $2 +; MIPS64R6O0-NEXT: sltiu $2, $1, 1 +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicCmpSwapRes8: +; MM32: # %bb.0: # %entry +; MM32-NEXT: addiu $1, $zero, -4 +; MM32-NEXT: and $2, $4, $1 +; MM32-NEXT: andi $1, $4, 3 +; MM32-NEXT: sll $3, $1, 3 +; MM32-NEXT: ori $1, $zero, 255 +; MM32-NEXT: sllv $4, $1, $3 +; MM32-NEXT: nor $7, $zero, $4 +; MM32-NEXT: andi $1, $5, 255 +; MM32-NEXT: sllv $8, $1, $3 +; MM32-NEXT: andi $1, $6, 255 +; MM32-NEXT: sllv $6, $1, $3 +; MM32-NEXT: $BB13_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $9, 0($2) +; MM32-NEXT: and $10, $9, $4 +; MM32-NEXT: bne $10, $8, $BB13_3 +; MM32-NEXT: nop +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: # in Loop: Header=BB13_1 Depth=1 +; MM32-NEXT: and $9, $9, $7 +; MM32-NEXT: or $9, $9, $6 +; MM32-NEXT: sc $9, 0($2) +; MM32-NEXT: beqzc $9, $BB13_1 +; MM32-NEXT: $BB13_3: # %entry +; MM32-NEXT: srlv $1, $10, $3 +; MM32-NEXT: seb $1, $1 +; MM32-NEXT: # %bb.4: # %entry +; MM32-NEXT: xor $1, $1, $5 +; MM32-NEXT: jr $ra +; MM32-NEXT: sltiu $2, $1, 1 +; +; O1-LABEL: AtomicCmpSwapRes8: +; O1: # %bb.0: # %entry +; O1-NEXT: addiu $1, $zero, -4 +; O1-NEXT: and $2, $4, $1 +; O1-NEXT: andi $1, $4, 3 +; O1-NEXT: sll $3, $1, 3 +; O1-NEXT: ori $1, $zero, 255 +; O1-NEXT: sllv $4, $1, $3 +; O1-NEXT: nor $7, $zero, $4 +; O1-NEXT: andi $1, $5, 255 +; O1-NEXT: sllv $8, $1, $3 +; O1-NEXT: andi $1, $6, 255 +; O1-NEXT: sllv $6, $1, $3 +; O1-NEXT: $BB13_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $9, 0($2) +; O1-NEXT: and $10, $9, $4 +; O1-NEXT: bne $10, $8, $BB13_3 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: # in Loop: Header=BB13_1 Depth=1 +; O1-NEXT: and $9, $9, $7 +; O1-NEXT: or $9, $9, $6 +; O1-NEXT: sc $9, 0($2) +; O1-NEXT: beqz $9, $BB13_1 +; O1-NEXT: nop +; O1-NEXT: $BB13_3: # %entry +; O1-NEXT: srlv $1, $10, $3 +; O1-NEXT: sll $1, $1, 24 +; O1-NEXT: sra $1, $1, 24 +; O1-NEXT: # %bb.4: # %entry +; O1-NEXT: sll $2, $5, 24 +; O1-NEXT: sra $2, $2, 24 +; O1-NEXT: xor $1, $1, $2 +; O1-NEXT: jr $ra +; O1-NEXT: sltiu $2, $1, 1 +; +; O2-LABEL: AtomicCmpSwapRes8: +; O2: # %bb.0: # %entry +; O2-NEXT: addiu $1, $zero, -4 +; O2-NEXT: and $2, $4, $1 +; O2-NEXT: andi $1, $4, 3 +; O2-NEXT: sll $3, $1, 3 +; O2-NEXT: ori $1, $zero, 255 +; O2-NEXT: sllv $4, $1, $3 +; O2-NEXT: nor $7, $zero, $4 +; O2-NEXT: andi $1, $5, 255 +; O2-NEXT: sllv $8, $1, $3 +; O2-NEXT: andi $1, $6, 255 +; O2-NEXT: sllv $6, $1, $3 +; O2-NEXT: $BB13_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $9, 0($2) +; O2-NEXT: and $10, $9, $4 +; O2-NEXT: bne $10, $8, $BB13_3 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: # in Loop: Header=BB13_1 Depth=1 +; O2-NEXT: and $9, $9, $7 +; O2-NEXT: or $9, $9, $6 +; O2-NEXT: sc $9, 0($2) +; O2-NEXT: beqz $9, $BB13_1 +; O2-NEXT: nop +; O2-NEXT: $BB13_3: # %entry +; O2-NEXT: srlv $1, $10, $3 +; O2-NEXT: sll $1, $1, 24 +; O2-NEXT: sra $1, $1, 24 +; O2-NEXT: # %bb.4: # %entry +; O2-NEXT: sll $2, $5, 24 +; O2-NEXT: sra $2, $2, 24 +; O2-NEXT: xor $1, $1, $2 +; O2-NEXT: jr $ra +; O2-NEXT: sltiu $2, $1, 1 +; +; O3-LABEL: AtomicCmpSwapRes8: +; O3: # %bb.0: # %entry +; O3-NEXT: addiu $1, $zero, -4 +; O3-NEXT: and $2, $4, $1 +; O3-NEXT: andi $1, $4, 3 +; O3-NEXT: sll $3, $1, 3 +; O3-NEXT: ori $1, $zero, 255 +; O3-NEXT: sllv $4, $1, $3 +; O3-NEXT: andi $1, $5, 255 +; O3-NEXT: sllv $8, $1, $3 +; O3-NEXT: andi $1, $6, 255 +; O3-NEXT: nor $7, $zero, $4 +; O3-NEXT: sllv $6, $1, $3 +; O3-NEXT: $BB13_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $9, 0($2) +; O3-NEXT: and $10, $9, $4 +; O3-NEXT: bne $10, $8, $BB13_3 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: # in Loop: Header=BB13_1 Depth=1 +; O3-NEXT: and $9, $9, $7 +; O3-NEXT: or $9, $9, $6 +; O3-NEXT: sc $9, 0($2) +; O3-NEXT: beqz $9, $BB13_1 +; O3-NEXT: nop +; O3-NEXT: $BB13_3: # %entry +; O3-NEXT: srlv $1, $10, $3 +; O3-NEXT: sll $1, $1, 24 +; O3-NEXT: sra $1, $1, 24 +; O3-NEXT: # %bb.4: # %entry +; O3-NEXT: sll $2, $5, 24 +; O3-NEXT: sra $2, $2, 24 +; O3-NEXT: xor $1, $1, $2 +; O3-NEXT: jr $ra +; O3-NEXT: sltiu $2, $1, 1 +; +; MIPS32EB-LABEL: AtomicCmpSwapRes8: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: addiu $1, $zero, -4 +; MIPS32EB-NEXT: and $2, $4, $1 +; MIPS32EB-NEXT: andi $1, $4, 3 +; MIPS32EB-NEXT: xori $1, $1, 3 +; MIPS32EB-NEXT: sll $3, $1, 3 +; MIPS32EB-NEXT: ori $1, $zero, 255 +; MIPS32EB-NEXT: sllv $4, $1, $3 +; MIPS32EB-NEXT: nor $7, $zero, $4 +; MIPS32EB-NEXT: andi $1, $5, 255 +; MIPS32EB-NEXT: sllv $8, $1, $3 +; MIPS32EB-NEXT: andi $1, $6, 255 +; MIPS32EB-NEXT: sllv $6, $1, $3 +; MIPS32EB-NEXT: $BB13_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $9, 0($2) +; MIPS32EB-NEXT: and $10, $9, $4 +; MIPS32EB-NEXT: bne $10, $8, $BB13_3 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: # in Loop: Header=BB13_1 Depth=1 +; MIPS32EB-NEXT: and $9, $9, $7 +; MIPS32EB-NEXT: or $9, $9, $6 +; MIPS32EB-NEXT: sc $9, 0($2) +; MIPS32EB-NEXT: beqz $9, $BB13_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: $BB13_3: # %entry +; MIPS32EB-NEXT: srlv $1, $10, $3 +; MIPS32EB-NEXT: sll $1, $1, 24 +; MIPS32EB-NEXT: sra $1, $1, 24 +; MIPS32EB-NEXT: # %bb.4: # %entry +; MIPS32EB-NEXT: sll $2, $5, 24 +; MIPS32EB-NEXT: sra $2, $2, 24 +; MIPS32EB-NEXT: xor $1, $1, $2 +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: sltiu $2, $1, 1 entry: %0 = cmpxchg i8* %ptr, i8 %oldval, i8 %newval monotonic monotonic %1 = extractvalue { i8, i1 } %0, 1 ret i1 %1 -; ALL-LABEL: AtomicCmpSwapRes8 - -; ALL: addiu $[[R1:[0-9]+]], $zero, -4 -; ALL: and $[[R2:[0-9]+]], $4, $[[R1]] -; ALL: andi $[[R3:[0-9]+]], $4, 3 -; CHECK-EL: sll $[[R5:[0-9]+]], $[[R3]], 3 -; CHECK-EB: xori $[[R4:[0-9]+]], $[[R3]], 3 -; CHECK-EB: sll $[[R5:[0-9]+]], $[[R4]], 3 -; ALL: ori $[[R6:[0-9]+]], $zero, 255 -; ALL: sllv $[[R7:[0-9]+]], $[[R6]], $[[R5]] -; ALL: nor $[[R8:[0-9]+]], $zero, $[[R7]] -; ALL: andi $[[R9:[0-9]+]], $5, 255 -; ALL: sllv $[[R10:[0-9]+]], $[[R9]], $[[R5]] -; ALL: andi $[[R11:[0-9]+]], $6, 255 -; ALL: sllv $[[R12:[0-9]+]], $[[R11]], $[[R5]] - -; ALL: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; ALL: ll $[[R13:[0-9]+]], 0($[[R2]]) -; ALL: and $[[R14:[0-9]+]], $[[R13]], $[[R7]] -; NOT-MICROMIPS: bne $[[R14]], $[[R10]], [[BB1:(\$|\.L)[A-Z_0-9]+]] -; MICROMIPS: bne $[[R14]], $[[R10]], [[BB1:(\$|\.L)[A-Z_0-9]+]] -; MIPSR6: bnec $[[R14]], $[[R10]], [[BB1:(\$|\.L)[A-Z_0-9]+]] - -; ALL: and $[[R15:[0-9]+]], $[[R13]], $[[R8]] -; ALL: or $[[R16:[0-9]+]], $[[R15]], $[[R12]] -; ALL: sc $[[R16]], 0($[[R2]]) -; NOT-MICROMIPS: beqz $[[R16]], [[BB0]] -; MICROMIPS: beqzc $[[R16]], [[BB0]] -; MIPSR6: beqzc $[[R16]], [[BB0]] - -; ALL: [[BB1]]: -; ALL: srlv $[[R17:[0-9]+]], $[[R14]], $[[R5]] - -; NO-SEB-SEH: sll $[[R18:[0-9]+]], $[[R17]], 24 -; NO-SEB-SEH: sra $[[R19:[0-9]+]], $[[R18]], 24 - ; FIXME: -march=mips produces a redundant sign extension here... -; NO-SEB-SEH: sll $[[R20:[0-9]+]], $5, 24 -; NO-SEB-SEH: sra $[[R20]], $[[R20]], 24 - -; HAS-SEB-SEH: seb $[[R19:[0-9]+]], $[[R17]] - ; FIXME: ...Leading to this split check. -; NO-SEB-SEH: xor $[[R21:[0-9]+]], $[[R19]], $[[R20]] -; HAS-SEB-SEH: xor $[[R21:[0-9]+]], $[[R19]], $5 -; ALL: sltiu $2, $[[R21]], 1 } ; Check one i16 so that we cover the seh sign extend @z = common global i16 0, align 1 define signext i16 @AtomicLoadAdd16(i16 signext %incr) nounwind { +; MIPS32-LABEL: AtomicLoadAdd16: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: lw $1, %got(z)($1) +; MIPS32-NEXT: addiu $2, $zero, -4 +; MIPS32-NEXT: and $2, $1, $2 +; MIPS32-NEXT: andi $1, $1, 3 +; MIPS32-NEXT: sll $3, $1, 3 +; MIPS32-NEXT: ori $1, $zero, 65535 +; MIPS32-NEXT: sllv $5, $1, $3 +; MIPS32-NEXT: nor $6, $zero, $5 +; MIPS32-NEXT: sllv $4, $4, $3 +; MIPS32-NEXT: $BB14_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $7, 0($2) +; MIPS32-NEXT: addu $8, $7, $4 +; MIPS32-NEXT: and $8, $8, $5 +; MIPS32-NEXT: and $9, $7, $6 +; MIPS32-NEXT: or $9, $9, $8 +; MIPS32-NEXT: sc $9, 0($2) +; MIPS32-NEXT: beqz $9, $BB14_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: and $1, $7, $5 +; MIPS32-NEXT: srlv $1, $1, $3 +; MIPS32-NEXT: sll $1, $1, 16 +; MIPS32-NEXT: sra $1, $1, 16 +; MIPS32-NEXT: # %bb.3: # %entry +; MIPS32-NEXT: sll $1, $1, 16 +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: sra $2, $1, 16 +; +; MIPS32O0-LABEL: AtomicLoadAdd16: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addiu $sp, $sp, -8 +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: lw $2, %got(z)($2) +; MIPS32O0-NEXT: addiu $25, $zero, -4 +; MIPS32O0-NEXT: and $25, $2, $25 +; MIPS32O0-NEXT: andi $2, $2, 3 +; MIPS32O0-NEXT: sll $2, $2, 3 +; MIPS32O0-NEXT: ori $1, $zero, 65535 +; MIPS32O0-NEXT: sllv $1, $1, $2 +; MIPS32O0-NEXT: nor $3, $zero, $1 +; MIPS32O0-NEXT: sllv $4, $4, $2 +; MIPS32O0-NEXT: $BB14_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $6, 0($25) +; MIPS32O0-NEXT: addu $7, $6, $4 +; MIPS32O0-NEXT: and $7, $7, $1 +; MIPS32O0-NEXT: and $8, $6, $3 +; MIPS32O0-NEXT: or $8, $8, $7 +; MIPS32O0-NEXT: sc $8, 0($25) +; MIPS32O0-NEXT: beqz $8, $BB14_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: and $5, $6, $1 +; MIPS32O0-NEXT: srlv $5, $5, $2 +; MIPS32O0-NEXT: sll $5, $5, 16 +; MIPS32O0-NEXT: sra $5, $5, 16 +; MIPS32O0-NEXT: # %bb.3: # %entry +; MIPS32O0-NEXT: sw $5, 4($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: # %bb.4: # %entry +; MIPS32O0-NEXT: lw $1, 4($sp) # 4-byte Folded Reload +; MIPS32O0-NEXT: sll $2, $1, 16 +; MIPS32O0-NEXT: sra $2, $2, 16 +; MIPS32O0-NEXT: addiu $sp, $sp, 8 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicLoadAdd16: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: lw $1, %got(z)($1) +; MIPS32R2-NEXT: addiu $2, $zero, -4 +; MIPS32R2-NEXT: and $2, $1, $2 +; MIPS32R2-NEXT: andi $1, $1, 3 +; MIPS32R2-NEXT: sll $3, $1, 3 +; MIPS32R2-NEXT: ori $1, $zero, 65535 +; MIPS32R2-NEXT: sllv $5, $1, $3 +; MIPS32R2-NEXT: nor $6, $zero, $5 +; MIPS32R2-NEXT: sllv $4, $4, $3 +; MIPS32R2-NEXT: $BB14_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $7, 0($2) +; MIPS32R2-NEXT: addu $8, $7, $4 +; MIPS32R2-NEXT: and $8, $8, $5 +; MIPS32R2-NEXT: and $9, $7, $6 +; MIPS32R2-NEXT: or $9, $9, $8 +; MIPS32R2-NEXT: sc $9, 0($2) +; MIPS32R2-NEXT: beqz $9, $BB14_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: and $1, $7, $5 +; MIPS32R2-NEXT: srlv $1, $1, $3 +; MIPS32R2-NEXT: seh $1, $1 +; MIPS32R2-NEXT: # %bb.3: # %entry +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: seh $2, $1 +; +; MIPS32R6-LABEL: AtomicLoadAdd16: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: lw $1, %got(z)($1) +; MIPS32R6-NEXT: addiu $2, $zero, -4 +; MIPS32R6-NEXT: and $2, $1, $2 +; MIPS32R6-NEXT: andi $1, $1, 3 +; MIPS32R6-NEXT: sll $3, $1, 3 +; MIPS32R6-NEXT: ori $1, $zero, 65535 +; MIPS32R6-NEXT: sllv $5, $1, $3 +; MIPS32R6-NEXT: nor $6, $zero, $5 +; MIPS32R6-NEXT: sllv $4, $4, $3 +; MIPS32R6-NEXT: $BB14_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $7, 0($2) +; MIPS32R6-NEXT: addu $8, $7, $4 +; MIPS32R6-NEXT: and $8, $8, $5 +; MIPS32R6-NEXT: and $9, $7, $6 +; MIPS32R6-NEXT: or $9, $9, $8 +; MIPS32R6-NEXT: sc $9, 0($2) +; MIPS32R6-NEXT: beqzc $9, $BB14_1 +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: and $1, $7, $5 +; MIPS32R6-NEXT: srlv $1, $1, $3 +; MIPS32R6-NEXT: seh $1, $1 +; MIPS32R6-NEXT: # %bb.3: # %entry +; MIPS32R6-NEXT: jr $ra +; MIPS32R6-NEXT: seh $2, $1 +; +; MIPS32R6O0-LABEL: AtomicLoadAdd16: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -8 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $4 +; MIPS32R6O0-NEXT: lw $2, %got(z)($2) +; MIPS32R6O0-NEXT: addiu $1, $zero, -4 +; MIPS32R6O0-NEXT: and $1, $2, $1 +; MIPS32R6O0-NEXT: andi $2, $2, 3 +; MIPS32R6O0-NEXT: sll $2, $2, 3 +; MIPS32R6O0-NEXT: ori $3, $zero, 65535 +; MIPS32R6O0-NEXT: sllv $3, $3, $2 +; MIPS32R6O0-NEXT: nor $5, $zero, $3 +; MIPS32R6O0-NEXT: sllv $4, $4, $2 +; MIPS32R6O0-NEXT: $BB14_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $7, 0($1) +; MIPS32R6O0-NEXT: addu $8, $7, $4 +; MIPS32R6O0-NEXT: and $8, $8, $3 +; MIPS32R6O0-NEXT: and $9, $7, $5 +; MIPS32R6O0-NEXT: or $9, $9, $8 +; MIPS32R6O0-NEXT: sc $9, 0($1) +; MIPS32R6O0-NEXT: beqzc $9, $BB14_1 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: and $6, $7, $3 +; MIPS32R6O0-NEXT: srlv $6, $6, $2 +; MIPS32R6O0-NEXT: seh $6, $6 +; MIPS32R6O0-NEXT: # %bb.3: # %entry +; MIPS32R6O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $6, 0($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: # %bb.4: # %entry +; MIPS32R6O0-NEXT: lw $1, 0($sp) # 4-byte Folded Reload +; MIPS32R6O0-NEXT: seh $2, $1 +; MIPS32R6O0-NEXT: addiu $sp, $sp, 8 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicLoadAdd16: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd16))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd16))) +; MIPS4-NEXT: ld $1, %got_disp(z)($1) +; MIPS4-NEXT: daddiu $2, $zero, -4 +; MIPS4-NEXT: and $2, $1, $2 +; MIPS4-NEXT: andi $1, $1, 3 +; MIPS4-NEXT: sll $3, $1, 3 +; MIPS4-NEXT: ori $1, $zero, 65535 +; MIPS4-NEXT: sllv $5, $1, $3 +; MIPS4-NEXT: nor $6, $zero, $5 +; MIPS4-NEXT: sllv $4, $4, $3 +; MIPS4-NEXT: .LBB14_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $7, 0($2) +; MIPS4-NEXT: addu $8, $7, $4 +; MIPS4-NEXT: and $8, $8, $5 +; MIPS4-NEXT: and $9, $7, $6 +; MIPS4-NEXT: or $9, $9, $8 +; MIPS4-NEXT: sc $9, 0($2) +; MIPS4-NEXT: beqz $9, .LBB14_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: and $1, $7, $5 +; MIPS4-NEXT: srlv $1, $1, $3 +; MIPS4-NEXT: sll $1, $1, 16 +; MIPS4-NEXT: sra $1, $1, 16 +; MIPS4-NEXT: # %bb.3: # %entry +; MIPS4-NEXT: sll $1, $1, 16 +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: sra $2, $1, 16 +; +; MIPS64-LABEL: AtomicLoadAdd16: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd16))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd16))) +; MIPS64-NEXT: ld $1, %got_disp(z)($1) +; MIPS64-NEXT: daddiu $2, $zero, -4 +; MIPS64-NEXT: and $2, $1, $2 +; MIPS64-NEXT: andi $1, $1, 3 +; MIPS64-NEXT: sll $3, $1, 3 +; MIPS64-NEXT: ori $1, $zero, 65535 +; MIPS64-NEXT: sllv $5, $1, $3 +; MIPS64-NEXT: nor $6, $zero, $5 +; MIPS64-NEXT: sllv $4, $4, $3 +; MIPS64-NEXT: .LBB14_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $7, 0($2) +; MIPS64-NEXT: addu $8, $7, $4 +; MIPS64-NEXT: and $8, $8, $5 +; MIPS64-NEXT: and $9, $7, $6 +; MIPS64-NEXT: or $9, $9, $8 +; MIPS64-NEXT: sc $9, 0($2) +; MIPS64-NEXT: beqz $9, .LBB14_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: and $1, $7, $5 +; MIPS64-NEXT: srlv $1, $1, $3 +; MIPS64-NEXT: sll $1, $1, 16 +; MIPS64-NEXT: sra $1, $1, 16 +; MIPS64-NEXT: # %bb.3: # %entry +; MIPS64-NEXT: sll $1, $1, 16 +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: sra $2, $1, 16 +; +; MIPS64R2-LABEL: AtomicLoadAdd16: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd16))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd16))) +; MIPS64R2-NEXT: ld $1, %got_disp(z)($1) +; MIPS64R2-NEXT: daddiu $2, $zero, -4 +; MIPS64R2-NEXT: and $2, $1, $2 +; MIPS64R2-NEXT: andi $1, $1, 3 +; MIPS64R2-NEXT: sll $3, $1, 3 +; MIPS64R2-NEXT: ori $1, $zero, 65535 +; MIPS64R2-NEXT: sllv $5, $1, $3 +; MIPS64R2-NEXT: nor $6, $zero, $5 +; MIPS64R2-NEXT: sllv $4, $4, $3 +; MIPS64R2-NEXT: .LBB14_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $7, 0($2) +; MIPS64R2-NEXT: addu $8, $7, $4 +; MIPS64R2-NEXT: and $8, $8, $5 +; MIPS64R2-NEXT: and $9, $7, $6 +; MIPS64R2-NEXT: or $9, $9, $8 +; MIPS64R2-NEXT: sc $9, 0($2) +; MIPS64R2-NEXT: beqz $9, .LBB14_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: and $1, $7, $5 +; MIPS64R2-NEXT: srlv $1, $1, $3 +; MIPS64R2-NEXT: seh $1, $1 +; MIPS64R2-NEXT: # %bb.3: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: seh $2, $1 +; +; MIPS64R6-LABEL: AtomicLoadAdd16: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd16))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd16))) +; MIPS64R6-NEXT: ld $1, %got_disp(z)($1) +; MIPS64R6-NEXT: daddiu $2, $zero, -4 +; MIPS64R6-NEXT: and $2, $1, $2 +; MIPS64R6-NEXT: andi $1, $1, 3 +; MIPS64R6-NEXT: sll $3, $1, 3 +; MIPS64R6-NEXT: ori $1, $zero, 65535 +; MIPS64R6-NEXT: sllv $5, $1, $3 +; MIPS64R6-NEXT: nor $6, $zero, $5 +; MIPS64R6-NEXT: sllv $4, $4, $3 +; MIPS64R6-NEXT: .LBB14_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $7, 0($2) +; MIPS64R6-NEXT: addu $8, $7, $4 +; MIPS64R6-NEXT: and $8, $8, $5 +; MIPS64R6-NEXT: and $9, $7, $6 +; MIPS64R6-NEXT: or $9, $9, $8 +; MIPS64R6-NEXT: sc $9, 0($2) +; MIPS64R6-NEXT: beqzc $9, .LBB14_1 +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: and $1, $7, $5 +; MIPS64R6-NEXT: srlv $1, $1, $3 +; MIPS64R6-NEXT: seh $1, $1 +; MIPS64R6-NEXT: # %bb.3: # %entry +; MIPS64R6-NEXT: jr $ra +; MIPS64R6-NEXT: seh $2, $1 +; +; MIPS64R6O0-LABEL: AtomicLoadAdd16: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd16))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd16))) +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(z)($1) +; MIPS64R6O0-NEXT: daddiu $4, $zero, -4 +; MIPS64R6O0-NEXT: and $4, $1, $4 +; MIPS64R6O0-NEXT: andi $3, $1, 3 +; MIPS64R6O0-NEXT: xori $3, $3, 2 +; MIPS64R6O0-NEXT: sll $3, $3, 3 +; MIPS64R6O0-NEXT: ori $5, $zero, 65535 +; MIPS64R6O0-NEXT: sllv $5, $5, $3 +; MIPS64R6O0-NEXT: nor $6, $zero, $5 +; MIPS64R6O0-NEXT: sllv $2, $2, $3 +; MIPS64R6O0-NEXT: .LBB14_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $8, 0($4) +; MIPS64R6O0-NEXT: addu $9, $8, $2 +; MIPS64R6O0-NEXT: and $9, $9, $5 +; MIPS64R6O0-NEXT: and $10, $8, $6 +; MIPS64R6O0-NEXT: or $10, $10, $9 +; MIPS64R6O0-NEXT: sc $10, 0($4) +; MIPS64R6O0-NEXT: beqzc $10, .LBB14_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: and $7, $8, $5 +; MIPS64R6O0-NEXT: srlv $7, $7, $3 +; MIPS64R6O0-NEXT: seh $7, $7 +; MIPS64R6O0-NEXT: # %bb.3: # %entry +; MIPS64R6O0-NEXT: sw $7, 12($sp) # 4-byte Folded Spill +; MIPS64R6O0-NEXT: # %bb.4: # %entry +; MIPS64R6O0-NEXT: lw $1, 12($sp) # 4-byte Folded Reload +; MIPS64R6O0-NEXT: seh $2, $1 +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicLoadAdd16: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: lw $1, %got(z)($2) +; MM32-NEXT: addiu $2, $zero, -4 +; MM32-NEXT: and $2, $1, $2 +; MM32-NEXT: andi $1, $1, 3 +; MM32-NEXT: sll $3, $1, 3 +; MM32-NEXT: ori $1, $zero, 65535 +; MM32-NEXT: sllv $5, $1, $3 +; MM32-NEXT: nor $6, $zero, $5 +; MM32-NEXT: sllv $4, $4, $3 +; MM32-NEXT: $BB14_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $7, 0($2) +; MM32-NEXT: addu $8, $7, $4 +; MM32-NEXT: and $8, $8, $5 +; MM32-NEXT: and $9, $7, $6 +; MM32-NEXT: or $9, $9, $8 +; MM32-NEXT: sc $9, 0($2) +; MM32-NEXT: beqzc $9, $BB14_1 +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: and $1, $7, $5 +; MM32-NEXT: srlv $1, $1, $3 +; MM32-NEXT: seh $1, $1 +; MM32-NEXT: # %bb.3: # %entry +; MM32-NEXT: jr $ra +; MM32-NEXT: seh $2, $1 +; +; O1-LABEL: AtomicLoadAdd16: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: lw $1, %got(z)($1) +; O1-NEXT: addiu $2, $zero, -4 +; O1-NEXT: and $2, $1, $2 +; O1-NEXT: andi $1, $1, 3 +; O1-NEXT: sll $3, $1, 3 +; O1-NEXT: ori $1, $zero, 65535 +; O1-NEXT: sllv $5, $1, $3 +; O1-NEXT: nor $6, $zero, $5 +; O1-NEXT: sllv $4, $4, $3 +; O1-NEXT: $BB14_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $7, 0($2) +; O1-NEXT: addu $8, $7, $4 +; O1-NEXT: and $8, $8, $5 +; O1-NEXT: and $9, $7, $6 +; O1-NEXT: or $9, $9, $8 +; O1-NEXT: sc $9, 0($2) +; O1-NEXT: beqz $9, $BB14_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: and $1, $7, $5 +; O1-NEXT: srlv $1, $1, $3 +; O1-NEXT: sll $1, $1, 16 +; O1-NEXT: sra $1, $1, 16 +; O1-NEXT: # %bb.3: # %entry +; O1-NEXT: sll $1, $1, 16 +; O1-NEXT: jr $ra +; O1-NEXT: sra $2, $1, 16 +; +; O2-LABEL: AtomicLoadAdd16: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: lw $1, %got(z)($1) +; O2-NEXT: addiu $2, $zero, -4 +; O2-NEXT: and $2, $1, $2 +; O2-NEXT: andi $1, $1, 3 +; O2-NEXT: sll $3, $1, 3 +; O2-NEXT: ori $1, $zero, 65535 +; O2-NEXT: sllv $5, $1, $3 +; O2-NEXT: nor $6, $zero, $5 +; O2-NEXT: sllv $4, $4, $3 +; O2-NEXT: $BB14_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $7, 0($2) +; O2-NEXT: addu $8, $7, $4 +; O2-NEXT: and $8, $8, $5 +; O2-NEXT: and $9, $7, $6 +; O2-NEXT: or $9, $9, $8 +; O2-NEXT: sc $9, 0($2) +; O2-NEXT: beqz $9, $BB14_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: and $1, $7, $5 +; O2-NEXT: srlv $1, $1, $3 +; O2-NEXT: sll $1, $1, 16 +; O2-NEXT: sra $1, $1, 16 +; O2-NEXT: # %bb.3: # %entry +; O2-NEXT: sll $1, $1, 16 +; O2-NEXT: jr $ra +; O2-NEXT: sra $2, $1, 16 +; +; O3-LABEL: AtomicLoadAdd16: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: addiu $2, $zero, -4 +; O3-NEXT: lw $1, %got(z)($1) +; O3-NEXT: and $2, $1, $2 +; O3-NEXT: andi $1, $1, 3 +; O3-NEXT: sll $3, $1, 3 +; O3-NEXT: ori $1, $zero, 65535 +; O3-NEXT: sllv $5, $1, $3 +; O3-NEXT: sllv $4, $4, $3 +; O3-NEXT: nor $6, $zero, $5 +; O3-NEXT: $BB14_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $7, 0($2) +; O3-NEXT: addu $8, $7, $4 +; O3-NEXT: and $8, $8, $5 +; O3-NEXT: and $9, $7, $6 +; O3-NEXT: or $9, $9, $8 +; O3-NEXT: sc $9, 0($2) +; O3-NEXT: beqz $9, $BB14_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: and $1, $7, $5 +; O3-NEXT: srlv $1, $1, $3 +; O3-NEXT: sll $1, $1, 16 +; O3-NEXT: sra $1, $1, 16 +; O3-NEXT: # %bb.3: # %entry +; O3-NEXT: sll $1, $1, 16 +; O3-NEXT: jr $ra +; O3-NEXT: sra $2, $1, 16 +; +; MIPS32EB-LABEL: AtomicLoadAdd16: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: lw $1, %got(z)($1) +; MIPS32EB-NEXT: addiu $2, $zero, -4 +; MIPS32EB-NEXT: and $2, $1, $2 +; MIPS32EB-NEXT: andi $1, $1, 3 +; MIPS32EB-NEXT: xori $1, $1, 2 +; MIPS32EB-NEXT: sll $3, $1, 3 +; MIPS32EB-NEXT: ori $1, $zero, 65535 +; MIPS32EB-NEXT: sllv $5, $1, $3 +; MIPS32EB-NEXT: nor $6, $zero, $5 +; MIPS32EB-NEXT: sllv $4, $4, $3 +; MIPS32EB-NEXT: $BB14_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $7, 0($2) +; MIPS32EB-NEXT: addu $8, $7, $4 +; MIPS32EB-NEXT: and $8, $8, $5 +; MIPS32EB-NEXT: and $9, $7, $6 +; MIPS32EB-NEXT: or $9, $9, $8 +; MIPS32EB-NEXT: sc $9, 0($2) +; MIPS32EB-NEXT: beqz $9, $BB14_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: and $1, $7, $5 +; MIPS32EB-NEXT: srlv $1, $1, $3 +; MIPS32EB-NEXT: sll $1, $1, 16 +; MIPS32EB-NEXT: sra $1, $1, 16 +; MIPS32EB-NEXT: # %bb.3: # %entry +; MIPS32EB-NEXT: sll $1, $1, 16 +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: sra $2, $1, 16 entry: %0 = atomicrmw add i16* @z, i16 %incr monotonic ret i16 %0 -; ALL-LABEL: AtomicLoadAdd16: - -; MIPS32-ANY: lw $[[R0:[0-9]+]], %got(z) -; MIPS64-ANY: ld $[[R0:[0-9]+]], %got_disp(z)( - -; ALL: addiu $[[R1:[0-9]+]], $zero, -4 -; ALL: and $[[R2:[0-9]+]], $[[R0]], $[[R1]] -; ALL: andi $[[R3:[0-9]+]], $[[R0]], 3 -; CHECK-EB: xori $[[R4:[0-9]+]], $[[R3]], 2 -; CHECK-EB: sll $[[R5:[0-9]+]], $[[R4]], 3 -; CHECK-EL: sll $[[R5:[0-9]+]], $[[R3]], 3 -; ALL: ori $[[R6:[0-9]+]], $zero, 65535 -; ALL: sllv $[[R7:[0-9]+]], $[[R6]], $[[R5]] -; ALL: nor $[[R8:[0-9]+]], $zero, $[[R7]] -; ALL: sllv $[[R9:[0-9]+]], $4, $[[R5]] - -; O0: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; O0: ld $[[R10:[0-9]+]] -; O0-NEXT: ll $[[R11:[0-9]+]], 0($[[R10]]) - -; ALL: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; ALL: ll $[[R12:[0-9]+]], 0($[[R2]]) -; ALL: addu $[[R13:[0-9]+]], $[[R12]], $[[R9]] -; ALL: and $[[R14:[0-9]+]], $[[R13]], $[[R7]] -; ALL: and $[[R15:[0-9]+]], $[[R12]], $[[R8]] -; ALL: or $[[R16:[0-9]+]], $[[R15]], $[[R14]] -; ALL: sc $[[R16]], 0($[[R2]]) -; NOT-MICROMIPS: beqz $[[R16]], [[BB0]] -; MICROMIPS: beqzc $[[R16]], [[BB0]] -; MIPSR6: beqzc $[[R16]], [[BB0]] - -; ALL: and $[[R17:[0-9]+]], $[[R12]], $[[R7]] -; ALL: srlv $[[R18:[0-9]+]], $[[R17]], $[[R5]] - -; NO-SEB-SEH: sll $[[R19:[0-9]+]], $[[R18]], 16 -; NO-SEB-SEH: sra $2, $[[R19]], 16 - -; MIPS32R2: seh $2, $[[R18]] } ; Test that the i16 return value from cmpxchg is recognised as signed, ; so that setCC doesn't end up comparing an unsigned value to a signed ; value. ; The rest of the functions here are testing the atomic expansion, so ; we just match the end of the function. define {i16, i1} @foo(i16* %addr, i16 %l, i16 %r, i16 %new) { +; MIPS32-LABEL: foo: +; MIPS32: # %bb.0: +; MIPS32-NEXT: addu $1, $5, $6 +; MIPS32-NEXT: sync +; MIPS32-NEXT: addiu $2, $zero, -4 +; MIPS32-NEXT: and $3, $4, $2 +; MIPS32-NEXT: andi $2, $4, 3 +; MIPS32-NEXT: sll $4, $2, 3 +; MIPS32-NEXT: ori $2, $zero, 65535 +; MIPS32-NEXT: sllv $5, $2, $4 +; MIPS32-NEXT: nor $6, $zero, $5 +; MIPS32-NEXT: andi $2, $1, 65535 +; MIPS32-NEXT: sllv $8, $2, $4 +; MIPS32-NEXT: andi $2, $7, 65535 +; MIPS32-NEXT: sllv $7, $2, $4 +; MIPS32-NEXT: $BB15_1: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $9, 0($3) +; MIPS32-NEXT: and $10, $9, $5 +; MIPS32-NEXT: bne $10, $8, $BB15_3 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # in Loop: Header=BB15_1 Depth=1 +; MIPS32-NEXT: and $9, $9, $6 +; MIPS32-NEXT: or $9, $9, $7 +; MIPS32-NEXT: sc $9, 0($3) +; MIPS32-NEXT: beqz $9, $BB15_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: $BB15_3: +; MIPS32-NEXT: srlv $2, $10, $4 +; MIPS32-NEXT: sll $2, $2, 16 +; MIPS32-NEXT: sra $2, $2, 16 +; MIPS32-NEXT: # %bb.4: +; MIPS32-NEXT: sll $1, $1, 16 +; MIPS32-NEXT: sra $1, $1, 16 +; MIPS32-NEXT: xor $1, $2, $1 +; MIPS32-NEXT: sltiu $3, $1, 1 +; MIPS32-NEXT: sync +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +; +; MIPS32O0-LABEL: foo: +; MIPS32O0: # %bb.0: +; MIPS32O0-NEXT: addiu $sp, $sp, -8 +; MIPS32O0-NEXT: .cfi_def_cfa_offset 8 +; MIPS32O0-NEXT: addu $5, $5, $6 +; MIPS32O0-NEXT: sync +; MIPS32O0-NEXT: addiu $6, $zero, -4 +; MIPS32O0-NEXT: and $6, $4, $6 +; MIPS32O0-NEXT: andi $4, $4, 3 +; MIPS32O0-NEXT: sll $4, $4, 3 +; MIPS32O0-NEXT: ori $1, $zero, 65535 +; MIPS32O0-NEXT: sllv $1, $1, $4 +; MIPS32O0-NEXT: nor $2, $zero, $1 +; MIPS32O0-NEXT: andi $3, $5, 65535 +; MIPS32O0-NEXT: sllv $3, $3, $4 +; MIPS32O0-NEXT: andi $7, $7, 65535 +; MIPS32O0-NEXT: sllv $7, $7, $4 +; MIPS32O0-NEXT: $BB15_1: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $9, 0($6) +; MIPS32O0-NEXT: and $10, $9, $1 +; MIPS32O0-NEXT: bne $10, $3, $BB15_3 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # in Loop: Header=BB15_1 Depth=1 +; MIPS32O0-NEXT: and $9, $9, $2 +; MIPS32O0-NEXT: or $9, $9, $7 +; MIPS32O0-NEXT: sc $9, 0($6) +; MIPS32O0-NEXT: beqz $9, $BB15_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: $BB15_3: +; MIPS32O0-NEXT: srlv $8, $10, $4 +; MIPS32O0-NEXT: sll $8, $8, 16 +; MIPS32O0-NEXT: sra $8, $8, 16 +; MIPS32O0-NEXT: # %bb.4: +; MIPS32O0-NEXT: sw $5, 4($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: sw $8, 0($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: # %bb.5: +; MIPS32O0-NEXT: lw $1, 4($sp) # 4-byte Folded Reload +; MIPS32O0-NEXT: sll $2, $1, 16 +; MIPS32O0-NEXT: sra $2, $2, 16 +; MIPS32O0-NEXT: lw $3, 0($sp) # 4-byte Folded Reload +; MIPS32O0-NEXT: xor $2, $3, $2 +; MIPS32O0-NEXT: sltiu $3, $2, 1 +; MIPS32O0-NEXT: sync +; MIPS32O0-NEXT: lw $2, 0($sp) # 4-byte Folded Reload +; MIPS32O0-NEXT: addiu $sp, $sp, 8 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: foo: +; MIPS32R2: # %bb.0: +; MIPS32R2-NEXT: addu $1, $5, $6 +; MIPS32R2-NEXT: sync +; MIPS32R2-NEXT: addiu $2, $zero, -4 +; MIPS32R2-NEXT: and $3, $4, $2 +; MIPS32R2-NEXT: andi $2, $4, 3 +; MIPS32R2-NEXT: sll $4, $2, 3 +; MIPS32R2-NEXT: ori $2, $zero, 65535 +; MIPS32R2-NEXT: sllv $5, $2, $4 +; MIPS32R2-NEXT: nor $6, $zero, $5 +; MIPS32R2-NEXT: andi $2, $1, 65535 +; MIPS32R2-NEXT: sllv $8, $2, $4 +; MIPS32R2-NEXT: andi $2, $7, 65535 +; MIPS32R2-NEXT: sllv $7, $2, $4 +; MIPS32R2-NEXT: $BB15_1: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $9, 0($3) +; MIPS32R2-NEXT: and $10, $9, $5 +; MIPS32R2-NEXT: bne $10, $8, $BB15_3 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # in Loop: Header=BB15_1 Depth=1 +; MIPS32R2-NEXT: and $9, $9, $6 +; MIPS32R2-NEXT: or $9, $9, $7 +; MIPS32R2-NEXT: sc $9, 0($3) +; MIPS32R2-NEXT: beqz $9, $BB15_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: $BB15_3: +; MIPS32R2-NEXT: srlv $2, $10, $4 +; MIPS32R2-NEXT: seh $2, $2 +; MIPS32R2-NEXT: # %bb.4: +; MIPS32R2-NEXT: seh $1, $1 +; MIPS32R2-NEXT: xor $1, $2, $1 +; MIPS32R2-NEXT: sltiu $3, $1, 1 +; MIPS32R2-NEXT: sync +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: nop +; +; MIPS32R6-LABEL: foo: +; MIPS32R6: # %bb.0: +; MIPS32R6-NEXT: addu $1, $5, $6 +; MIPS32R6-NEXT: sync +; MIPS32R6-NEXT: addiu $2, $zero, -4 +; MIPS32R6-NEXT: and $3, $4, $2 +; MIPS32R6-NEXT: andi $2, $4, 3 +; MIPS32R6-NEXT: sll $4, $2, 3 +; MIPS32R6-NEXT: ori $2, $zero, 65535 +; MIPS32R6-NEXT: sllv $5, $2, $4 +; MIPS32R6-NEXT: nor $6, $zero, $5 +; MIPS32R6-NEXT: andi $2, $1, 65535 +; MIPS32R6-NEXT: sllv $8, $2, $4 +; MIPS32R6-NEXT: andi $2, $7, 65535 +; MIPS32R6-NEXT: sllv $7, $2, $4 +; MIPS32R6-NEXT: $BB15_1: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $9, 0($3) +; MIPS32R6-NEXT: and $10, $9, $5 +; MIPS32R6-NEXT: bnec $10, $8, $BB15_3 +; MIPS32R6-NEXT: # %bb.2: # in Loop: Header=BB15_1 Depth=1 +; MIPS32R6-NEXT: and $9, $9, $6 +; MIPS32R6-NEXT: or $9, $9, $7 +; MIPS32R6-NEXT: sc $9, 0($3) +; MIPS32R6-NEXT: beqzc $9, $BB15_1 +; MIPS32R6-NEXT: $BB15_3: +; MIPS32R6-NEXT: srlv $2, $10, $4 +; MIPS32R6-NEXT: seh $2, $2 +; MIPS32R6-NEXT: # %bb.4: +; MIPS32R6-NEXT: seh $1, $1 +; MIPS32R6-NEXT: xor $1, $2, $1 +; MIPS32R6-NEXT: sltiu $3, $1, 1 +; MIPS32R6-NEXT: sync +; MIPS32R6-NEXT: jrc $ra +; +; MIPS32R6O0-LABEL: foo: +; MIPS32R6O0: # %bb.0: +; MIPS32R6O0-NEXT: addiu $sp, $sp, -24 +; MIPS32R6O0-NEXT: .cfi_def_cfa_offset 24 +; MIPS32R6O0-NEXT: move $1, $7 +; MIPS32R6O0-NEXT: move $2, $6 +; MIPS32R6O0-NEXT: move $3, $5 +; MIPS32R6O0-NEXT: move $8, $4 +; MIPS32R6O0-NEXT: addu $5, $5, $6 +; MIPS32R6O0-NEXT: sync +; MIPS32R6O0-NEXT: addiu $6, $zero, -4 +; MIPS32R6O0-NEXT: and $6, $4, $6 +; MIPS32R6O0-NEXT: andi $4, $4, 3 +; MIPS32R6O0-NEXT: sll $4, $4, 3 +; MIPS32R6O0-NEXT: ori $9, $zero, 65535 +; MIPS32R6O0-NEXT: sllv $9, $9, $4 +; MIPS32R6O0-NEXT: nor $10, $zero, $9 +; MIPS32R6O0-NEXT: andi $11, $5, 65535 +; MIPS32R6O0-NEXT: sllv $11, $11, $4 +; MIPS32R6O0-NEXT: andi $7, $7, 65535 +; MIPS32R6O0-NEXT: sllv $7, $7, $4 +; MIPS32R6O0-NEXT: $BB15_1: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $13, 0($6) +; MIPS32R6O0-NEXT: and $14, $13, $9 +; MIPS32R6O0-NEXT: bnec $14, $11, $BB15_3 +; MIPS32R6O0-NEXT: # %bb.2: # in Loop: Header=BB15_1 Depth=1 +; MIPS32R6O0-NEXT: and $13, $13, $10 +; MIPS32R6O0-NEXT: or $13, $13, $7 +; MIPS32R6O0-NEXT: sc $13, 0($6) +; MIPS32R6O0-NEXT: beqzc $13, $BB15_1 +; MIPS32R6O0-NEXT: $BB15_3: +; MIPS32R6O0-NEXT: srlv $12, $14, $4 +; MIPS32R6O0-NEXT: seh $12, $12 +; MIPS32R6O0-NEXT: # %bb.4: +; MIPS32R6O0-NEXT: sw $12, 20($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $3, 16($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $8, 12($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $5, 8($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $1, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $2, 0($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: # %bb.5: +; MIPS32R6O0-NEXT: lw $1, 8($sp) # 4-byte Folded Reload +; MIPS32R6O0-NEXT: seh $2, $1 +; MIPS32R6O0-NEXT: lw $3, 20($sp) # 4-byte Folded Reload +; MIPS32R6O0-NEXT: xor $2, $3, $2 +; MIPS32R6O0-NEXT: sltiu $3, $2, 1 +; MIPS32R6O0-NEXT: sync +; MIPS32R6O0-NEXT: lw $2, 20($sp) # 4-byte Folded Reload +; MIPS32R6O0-NEXT: addiu $sp, $sp, 24 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: foo: +; MIPS4: # %bb.0: +; MIPS4-NEXT: sll $1, $6, 0 +; MIPS4-NEXT: sll $2, $5, 0 +; MIPS4-NEXT: addu $1, $2, $1 +; MIPS4-NEXT: sync +; MIPS4-NEXT: sll $2, $7, 0 +; MIPS4-NEXT: daddiu $3, $zero, -4 +; MIPS4-NEXT: and $3, $4, $3 +; MIPS4-NEXT: andi $4, $4, 3 +; MIPS4-NEXT: sll $4, $4, 3 +; MIPS4-NEXT: ori $5, $zero, 65535 +; MIPS4-NEXT: sllv $5, $5, $4 +; MIPS4-NEXT: nor $6, $zero, $5 +; MIPS4-NEXT: andi $7, $1, 65535 +; MIPS4-NEXT: sllv $7, $7, $4 +; MIPS4-NEXT: andi $2, $2, 65535 +; MIPS4-NEXT: sllv $8, $2, $4 +; MIPS4-NEXT: .LBB15_1: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $9, 0($3) +; MIPS4-NEXT: and $10, $9, $5 +; MIPS4-NEXT: bne $10, $7, .LBB15_3 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # in Loop: Header=BB15_1 Depth=1 +; MIPS4-NEXT: and $9, $9, $6 +; MIPS4-NEXT: or $9, $9, $8 +; MIPS4-NEXT: sc $9, 0($3) +; MIPS4-NEXT: beqz $9, .LBB15_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: .LBB15_3: +; MIPS4-NEXT: srlv $2, $10, $4 +; MIPS4-NEXT: sll $2, $2, 16 +; MIPS4-NEXT: sra $2, $2, 16 +; MIPS4-NEXT: # %bb.4: +; MIPS4-NEXT: sll $1, $1, 16 +; MIPS4-NEXT: sra $1, $1, 16 +; MIPS4-NEXT: xor $1, $2, $1 +; MIPS4-NEXT: sltiu $3, $1, 1 +; MIPS4-NEXT: sync +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: foo: +; MIPS64: # %bb.0: +; MIPS64-NEXT: sll $1, $6, 0 +; MIPS64-NEXT: sll $2, $5, 0 +; MIPS64-NEXT: addu $1, $2, $1 +; MIPS64-NEXT: sync +; MIPS64-NEXT: sll $2, $7, 0 +; MIPS64-NEXT: daddiu $3, $zero, -4 +; MIPS64-NEXT: and $3, $4, $3 +; MIPS64-NEXT: andi $4, $4, 3 +; MIPS64-NEXT: sll $4, $4, 3 +; MIPS64-NEXT: ori $5, $zero, 65535 +; MIPS64-NEXT: sllv $5, $5, $4 +; MIPS64-NEXT: nor $6, $zero, $5 +; MIPS64-NEXT: andi $7, $1, 65535 +; MIPS64-NEXT: sllv $7, $7, $4 +; MIPS64-NEXT: andi $2, $2, 65535 +; MIPS64-NEXT: sllv $8, $2, $4 +; MIPS64-NEXT: .LBB15_1: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $9, 0($3) +; MIPS64-NEXT: and $10, $9, $5 +; MIPS64-NEXT: bne $10, $7, .LBB15_3 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # in Loop: Header=BB15_1 Depth=1 +; MIPS64-NEXT: and $9, $9, $6 +; MIPS64-NEXT: or $9, $9, $8 +; MIPS64-NEXT: sc $9, 0($3) +; MIPS64-NEXT: beqz $9, .LBB15_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: .LBB15_3: +; MIPS64-NEXT: srlv $2, $10, $4 +; MIPS64-NEXT: sll $2, $2, 16 +; MIPS64-NEXT: sra $2, $2, 16 +; MIPS64-NEXT: # %bb.4: +; MIPS64-NEXT: sll $1, $1, 16 +; MIPS64-NEXT: sra $1, $1, 16 +; MIPS64-NEXT: xor $1, $2, $1 +; MIPS64-NEXT: sltiu $3, $1, 1 +; MIPS64-NEXT: sync +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: foo: +; MIPS64R2: # %bb.0: +; MIPS64R2-NEXT: sll $1, $6, 0 +; MIPS64R2-NEXT: sll $2, $5, 0 +; MIPS64R2-NEXT: addu $1, $2, $1 +; MIPS64R2-NEXT: sync +; MIPS64R2-NEXT: sll $2, $7, 0 +; MIPS64R2-NEXT: daddiu $3, $zero, -4 +; MIPS64R2-NEXT: and $3, $4, $3 +; MIPS64R2-NEXT: andi $4, $4, 3 +; MIPS64R2-NEXT: sll $4, $4, 3 +; MIPS64R2-NEXT: ori $5, $zero, 65535 +; MIPS64R2-NEXT: sllv $5, $5, $4 +; MIPS64R2-NEXT: nor $6, $zero, $5 +; MIPS64R2-NEXT: andi $7, $1, 65535 +; MIPS64R2-NEXT: sllv $7, $7, $4 +; MIPS64R2-NEXT: andi $2, $2, 65535 +; MIPS64R2-NEXT: sllv $8, $2, $4 +; MIPS64R2-NEXT: .LBB15_1: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $9, 0($3) +; MIPS64R2-NEXT: and $10, $9, $5 +; MIPS64R2-NEXT: bne $10, $7, .LBB15_3 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # in Loop: Header=BB15_1 Depth=1 +; MIPS64R2-NEXT: and $9, $9, $6 +; MIPS64R2-NEXT: or $9, $9, $8 +; MIPS64R2-NEXT: sc $9, 0($3) +; MIPS64R2-NEXT: beqz $9, .LBB15_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: .LBB15_3: +; MIPS64R2-NEXT: srlv $2, $10, $4 +; MIPS64R2-NEXT: seh $2, $2 +; MIPS64R2-NEXT: # %bb.4: +; MIPS64R2-NEXT: seh $1, $1 +; MIPS64R2-NEXT: xor $1, $2, $1 +; MIPS64R2-NEXT: sltiu $3, $1, 1 +; MIPS64R2-NEXT: sync +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: foo: +; MIPS64R6: # %bb.0: +; MIPS64R6-NEXT: sll $1, $6, 0 +; MIPS64R6-NEXT: sll $2, $5, 0 +; MIPS64R6-NEXT: addu $1, $2, $1 +; MIPS64R6-NEXT: sync +; MIPS64R6-NEXT: sll $2, $7, 0 +; MIPS64R6-NEXT: daddiu $3, $zero, -4 +; MIPS64R6-NEXT: and $3, $4, $3 +; MIPS64R6-NEXT: andi $4, $4, 3 +; MIPS64R6-NEXT: sll $4, $4, 3 +; MIPS64R6-NEXT: ori $5, $zero, 65535 +; MIPS64R6-NEXT: sllv $5, $5, $4 +; MIPS64R6-NEXT: nor $6, $zero, $5 +; MIPS64R6-NEXT: andi $7, $1, 65535 +; MIPS64R6-NEXT: sllv $7, $7, $4 +; MIPS64R6-NEXT: andi $2, $2, 65535 +; MIPS64R6-NEXT: sllv $8, $2, $4 +; MIPS64R6-NEXT: .LBB15_1: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $9, 0($3) +; MIPS64R6-NEXT: and $10, $9, $5 +; MIPS64R6-NEXT: bnec $10, $7, .LBB15_3 +; MIPS64R6-NEXT: # %bb.2: # in Loop: Header=BB15_1 Depth=1 +; MIPS64R6-NEXT: and $9, $9, $6 +; MIPS64R6-NEXT: or $9, $9, $8 +; MIPS64R6-NEXT: sc $9, 0($3) +; MIPS64R6-NEXT: beqzc $9, .LBB15_1 +; MIPS64R6-NEXT: .LBB15_3: +; MIPS64R6-NEXT: srlv $2, $10, $4 +; MIPS64R6-NEXT: seh $2, $2 +; MIPS64R6-NEXT: # %bb.4: +; MIPS64R6-NEXT: seh $1, $1 +; MIPS64R6-NEXT: xor $1, $2, $1 +; MIPS64R6-NEXT: sltiu $3, $1, 1 +; MIPS64R6-NEXT: sync +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: foo: +; MIPS64R6O0: # %bb.0: +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: .cfi_def_cfa_offset 16 +; MIPS64R6O0-NEXT: move $1, $7 +; MIPS64R6O0-NEXT: sll $1, $1, 0 +; MIPS64R6O0-NEXT: move $2, $6 +; MIPS64R6O0-NEXT: sll $2, $2, 0 +; MIPS64R6O0-NEXT: move $3, $5 +; MIPS64R6O0-NEXT: sll $3, $3, 0 +; MIPS64R6O0-NEXT: move $5, $4 +; MIPS64R6O0-NEXT: addu $2, $3, $2 +; MIPS64R6O0-NEXT: sync +; MIPS64R6O0-NEXT: daddiu $6, $zero, -4 +; MIPS64R6O0-NEXT: and $6, $4, $6 +; MIPS64R6O0-NEXT: andi $3, $4, 3 +; MIPS64R6O0-NEXT: xori $3, $3, 2 +; MIPS64R6O0-NEXT: sll $3, $3, 3 +; MIPS64R6O0-NEXT: ori $8, $zero, 65535 +; MIPS64R6O0-NEXT: sllv $8, $8, $3 +; MIPS64R6O0-NEXT: nor $9, $zero, $8 +; MIPS64R6O0-NEXT: andi $10, $2, 65535 +; MIPS64R6O0-NEXT: sllv $10, $10, $3 +; MIPS64R6O0-NEXT: andi $1, $1, 65535 +; MIPS64R6O0-NEXT: sllv $1, $1, $3 +; MIPS64R6O0-NEXT: .LBB15_1: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $12, 0($6) +; MIPS64R6O0-NEXT: and $13, $12, $8 +; MIPS64R6O0-NEXT: bnec $13, $10, .LBB15_3 +; MIPS64R6O0-NEXT: # %bb.2: # in Loop: Header=BB15_1 Depth=1 +; MIPS64R6O0-NEXT: and $12, $12, $9 +; MIPS64R6O0-NEXT: or $12, $12, $1 +; MIPS64R6O0-NEXT: sc $12, 0($6) +; MIPS64R6O0-NEXT: beqzc $12, .LBB15_1 +; MIPS64R6O0-NEXT: .LBB15_3: +; MIPS64R6O0-NEXT: srlv $11, $13, $3 +; MIPS64R6O0-NEXT: seh $11, $11 +; MIPS64R6O0-NEXT: # %bb.4: +; MIPS64R6O0-NEXT: sw $2, 12($sp) # 4-byte Folded Spill +; MIPS64R6O0-NEXT: sw $11, 8($sp) # 4-byte Folded Spill +; MIPS64R6O0-NEXT: sd $5, 0($sp) # 8-byte Folded Spill +; MIPS64R6O0-NEXT: # %bb.5: +; MIPS64R6O0-NEXT: lw $1, 12($sp) # 4-byte Folded Reload +; MIPS64R6O0-NEXT: seh $2, $1 +; MIPS64R6O0-NEXT: lw $3, 8($sp) # 4-byte Folded Reload +; MIPS64R6O0-NEXT: xor $2, $3, $2 +; MIPS64R6O0-NEXT: sltiu $3, $2, 1 +; MIPS64R6O0-NEXT: sync +; MIPS64R6O0-NEXT: lw $2, 8($sp) # 4-byte Folded Reload +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: foo: +; MM32: # %bb.0: +; MM32-NEXT: addu16 $3, $5, $6 +; MM32-NEXT: sync +; MM32-NEXT: addiu $1, $zero, -4 +; MM32-NEXT: and $1, $4, $1 +; MM32-NEXT: andi $2, $4, 3 +; MM32-NEXT: sll $4, $2, 3 +; MM32-NEXT: ori $2, $zero, 65535 +; MM32-NEXT: sllv $5, $2, $4 +; MM32-NEXT: nor $6, $zero, $5 +; MM32-NEXT: andi $2, $3, 65535 +; MM32-NEXT: sllv $8, $2, $4 +; MM32-NEXT: andi $2, $7, 65535 +; MM32-NEXT: sllv $7, $2, $4 +; MM32-NEXT: $BB15_1: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $9, 0($1) +; MM32-NEXT: and $10, $9, $5 +; MM32-NEXT: bne $10, $8, $BB15_3 +; MM32-NEXT: nop +; MM32-NEXT: # %bb.2: # in Loop: Header=BB15_1 Depth=1 +; MM32-NEXT: and $9, $9, $6 +; MM32-NEXT: or $9, $9, $7 +; MM32-NEXT: sc $9, 0($1) +; MM32-NEXT: beqzc $9, $BB15_1 +; MM32-NEXT: $BB15_3: +; MM32-NEXT: srlv $2, $10, $4 +; MM32-NEXT: seh $2, $2 +; MM32-NEXT: # %bb.4: +; MM32-NEXT: seh $1, $3 +; MM32-NEXT: xor $1, $2, $1 +; MM32-NEXT: sltiu $3, $1, 1 +; MM32-NEXT: sync +; MM32-NEXT: jrc $ra +; +; O1-LABEL: foo: +; O1: # %bb.0: +; O1-NEXT: addu $1, $5, $6 +; O1-NEXT: sync +; O1-NEXT: addiu $2, $zero, -4 +; O1-NEXT: and $3, $4, $2 +; O1-NEXT: andi $2, $4, 3 +; O1-NEXT: sll $4, $2, 3 +; O1-NEXT: ori $2, $zero, 65535 +; O1-NEXT: sllv $5, $2, $4 +; O1-NEXT: nor $6, $zero, $5 +; O1-NEXT: andi $2, $1, 65535 +; O1-NEXT: sllv $8, $2, $4 +; O1-NEXT: andi $2, $7, 65535 +; O1-NEXT: sllv $7, $2, $4 +; O1-NEXT: $BB15_1: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $9, 0($3) +; O1-NEXT: and $10, $9, $5 +; O1-NEXT: bne $10, $8, $BB15_3 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # in Loop: Header=BB15_1 Depth=1 +; O1-NEXT: and $9, $9, $6 +; O1-NEXT: or $9, $9, $7 +; O1-NEXT: sc $9, 0($3) +; O1-NEXT: beqz $9, $BB15_1 +; O1-NEXT: nop +; O1-NEXT: $BB15_3: +; O1-NEXT: srlv $2, $10, $4 +; O1-NEXT: sll $2, $2, 16 +; O1-NEXT: sra $2, $2, 16 +; O1-NEXT: # %bb.4: +; O1-NEXT: sll $1, $1, 16 +; O1-NEXT: sra $1, $1, 16 +; O1-NEXT: xor $1, $2, $1 +; O1-NEXT: sltiu $3, $1, 1 +; O1-NEXT: sync +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: foo: +; O2: # %bb.0: +; O2-NEXT: addu $1, $5, $6 +; O2-NEXT: sync +; O2-NEXT: addiu $2, $zero, -4 +; O2-NEXT: and $3, $4, $2 +; O2-NEXT: andi $2, $4, 3 +; O2-NEXT: sll $4, $2, 3 +; O2-NEXT: ori $2, $zero, 65535 +; O2-NEXT: sllv $5, $2, $4 +; O2-NEXT: nor $6, $zero, $5 +; O2-NEXT: andi $2, $1, 65535 +; O2-NEXT: sllv $8, $2, $4 +; O2-NEXT: andi $2, $7, 65535 +; O2-NEXT: sllv $7, $2, $4 +; O2-NEXT: $BB15_1: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $9, 0($3) +; O2-NEXT: and $10, $9, $5 +; O2-NEXT: bne $10, $8, $BB15_3 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # in Loop: Header=BB15_1 Depth=1 +; O2-NEXT: and $9, $9, $6 +; O2-NEXT: or $9, $9, $7 +; O2-NEXT: sc $9, 0($3) +; O2-NEXT: beqz $9, $BB15_1 +; O2-NEXT: nop +; O2-NEXT: $BB15_3: +; O2-NEXT: srlv $2, $10, $4 +; O2-NEXT: sll $2, $2, 16 +; O2-NEXT: sra $2, $2, 16 +; O2-NEXT: # %bb.4: +; O2-NEXT: sll $1, $1, 16 +; O2-NEXT: sra $1, $1, 16 +; O2-NEXT: xor $1, $2, $1 +; O2-NEXT: sltiu $3, $1, 1 +; O2-NEXT: sync +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: foo: +; O3: # %bb.0: +; O3-NEXT: addiu $2, $zero, -4 +; O3-NEXT: addu $1, $5, $6 +; O3-NEXT: sync +; O3-NEXT: and $3, $4, $2 +; O3-NEXT: andi $2, $4, 3 +; O3-NEXT: sll $4, $2, 3 +; O3-NEXT: ori $2, $zero, 65535 +; O3-NEXT: sllv $5, $2, $4 +; O3-NEXT: andi $2, $1, 65535 +; O3-NEXT: sll $1, $1, 16 +; O3-NEXT: sllv $8, $2, $4 +; O3-NEXT: andi $2, $7, 65535 +; O3-NEXT: nor $6, $zero, $5 +; O3-NEXT: sra $1, $1, 16 +; O3-NEXT: sllv $7, $2, $4 +; O3-NEXT: $BB15_1: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $9, 0($3) +; O3-NEXT: and $10, $9, $5 +; O3-NEXT: bne $10, $8, $BB15_3 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # in Loop: Header=BB15_1 Depth=1 +; O3-NEXT: and $9, $9, $6 +; O3-NEXT: or $9, $9, $7 +; O3-NEXT: sc $9, 0($3) +; O3-NEXT: beqz $9, $BB15_1 +; O3-NEXT: nop +; O3-NEXT: $BB15_3: +; O3-NEXT: srlv $2, $10, $4 +; O3-NEXT: sll $2, $2, 16 +; O3-NEXT: sra $2, $2, 16 +; O3-NEXT: # %bb.4: +; O3-NEXT: sync +; O3-NEXT: xor $1, $2, $1 +; O3-NEXT: jr $ra +; O3-NEXT: sltiu $3, $1, 1 +; +; MIPS32EB-LABEL: foo: +; MIPS32EB: # %bb.0: +; MIPS32EB-NEXT: addu $1, $5, $6 +; MIPS32EB-NEXT: sync +; MIPS32EB-NEXT: addiu $2, $zero, -4 +; MIPS32EB-NEXT: and $3, $4, $2 +; MIPS32EB-NEXT: andi $2, $4, 3 +; MIPS32EB-NEXT: xori $2, $2, 2 +; MIPS32EB-NEXT: sll $4, $2, 3 +; MIPS32EB-NEXT: ori $2, $zero, 65535 +; MIPS32EB-NEXT: sllv $5, $2, $4 +; MIPS32EB-NEXT: nor $6, $zero, $5 +; MIPS32EB-NEXT: andi $2, $1, 65535 +; MIPS32EB-NEXT: sllv $8, $2, $4 +; MIPS32EB-NEXT: andi $2, $7, 65535 +; MIPS32EB-NEXT: sllv $7, $2, $4 +; MIPS32EB-NEXT: $BB15_1: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $9, 0($3) +; MIPS32EB-NEXT: and $10, $9, $5 +; MIPS32EB-NEXT: bne $10, $8, $BB15_3 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # in Loop: Header=BB15_1 Depth=1 +; MIPS32EB-NEXT: and $9, $9, $6 +; MIPS32EB-NEXT: or $9, $9, $7 +; MIPS32EB-NEXT: sc $9, 0($3) +; MIPS32EB-NEXT: beqz $9, $BB15_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: $BB15_3: +; MIPS32EB-NEXT: srlv $2, $10, $4 +; MIPS32EB-NEXT: sll $2, $2, 16 +; MIPS32EB-NEXT: sra $2, $2, 16 +; MIPS32EB-NEXT: # %bb.4: +; MIPS32EB-NEXT: sll $1, $1, 16 +; MIPS32EB-NEXT: sra $1, $1, 16 +; MIPS32EB-NEXT: xor $1, $2, $1 +; MIPS32EB-NEXT: sltiu $3, $1, 1 +; MIPS32EB-NEXT: sync +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: nop %desired = add i16 %l, %r %res = cmpxchg i16* %addr, i16 %desired, i16 %new seq_cst seq_cst ret {i16, i1} %res - -; ALL-LABEL: foo -; MIPSR6: addu $[[R2:[0-9]+]], $[[R1:[0-9]+]], $[[R0:[0-9]+]] -; NOT-MICROMIPS: addu $[[R2:[0-9]+]], $[[R1:[0-9]+]], $[[R0:[0-9]+]] -; MICROMIPS: addu16 $[[R2:[0-9]+]], $[[R1:[0-9]+]], $[[R0:[0-9]+]] - -; ALL: sync - -; ALL: andi $[[R3:[0-9]+]], $[[R2]], 65535 -; ALL: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; ALL: ll $[[R4:[0-9]+]], 0($[[R5:[0-9]+]]) -; ALL: and $[[R6:[0-9]+]], $[[R4]], $ -; ALL: and $[[R7:[0-9]+]], $[[R4]], $ -; ALL: or $[[R8:[0-9]+]], $[[R7]], $ -; ALL: sc $[[R8]], 0($[[R5]]) -; NOT-MICROMIPS: beqz $[[R8]], [[BB0]] -; MICROMIPS: beqzc $[[R8]], [[BB0]] -; MIPSR6: beqzc $[[R8]], [[BB0]] - -; ALL: srlv $[[R9:[0-9]+]], $[[R6]], $ - -; NO-SEB-SEH: sll $[[R10:[0-9]+]], $[[R9]], 16 -; NO-SEB-SEH: sra $[[R11:[0-9]+]], $[[R10]], 16 - -; NO-SEB-SEH: sll $[[R12:[0-9]+]], $[[R2]], 16 -; NO-SEB-SEH: sra $[[R13:[0-9]+]], $[[R12]], 16 - -; HAS-SEB-SEH: seh $[[R11:[0-9]+]], $[[R9]] -; HAS-SEB-SEH: seh $[[R13:[0-9]+]], $[[R2]] - -; ALL: xor $[[R12:[0-9]+]], $[[R11]], $[[R13]] -; ALL: sltiu $3, $[[R12]], 1 -; ALL: sync } @countsint = common global i32 0, align 4 define i32 @CheckSync(i32 signext %v) nounwind noinline { +; MIPS32-LABEL: CheckSync: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: sync +; MIPS32-NEXT: lw $1, %got(countsint)($1) +; MIPS32-NEXT: $BB16_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $2, 0($1) +; MIPS32-NEXT: addu $3, $2, $4 +; MIPS32-NEXT: sc $3, 0($1) +; MIPS32-NEXT: beqz $3, $BB16_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: sync +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +; +; MIPS32O0-LABEL: CheckSync: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: sync +; MIPS32O0-NEXT: lw $2, %got(countsint)($2) +; MIPS32O0-NEXT: $BB16_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $25, 0($2) +; MIPS32O0-NEXT: addu $1, $25, $4 +; MIPS32O0-NEXT: sc $1, 0($2) +; MIPS32O0-NEXT: beqz $1, $BB16_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: sync +; MIPS32O0-NEXT: move $2, $25 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: CheckSync: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: sync +; MIPS32R2-NEXT: lw $1, %got(countsint)($1) +; MIPS32R2-NEXT: $BB16_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $2, 0($1) +; MIPS32R2-NEXT: addu $3, $2, $4 +; MIPS32R2-NEXT: sc $3, 0($1) +; MIPS32R2-NEXT: beqz $3, $BB16_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: sync +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: nop +; +; MIPS32R6-LABEL: CheckSync: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: sync +; MIPS32R6-NEXT: lw $1, %got(countsint)($1) +; MIPS32R6-NEXT: $BB16_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $2, 0($1) +; MIPS32R6-NEXT: addu $3, $2, $4 +; MIPS32R6-NEXT: sc $3, 0($1) +; MIPS32R6-NEXT: beqzc $3, $BB16_1 +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: sync +; MIPS32R6-NEXT: jrc $ra +; +; MIPS32R6O0-LABEL: CheckSync: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -8 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $4 +; MIPS32R6O0-NEXT: sync +; MIPS32R6O0-NEXT: lw $2, %got(countsint)($2) +; MIPS32R6O0-NEXT: $BB16_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $1, 0($2) +; MIPS32R6O0-NEXT: addu $3, $1, $4 +; MIPS32R6O0-NEXT: sc $3, 0($2) +; MIPS32R6O0-NEXT: beqzc $3, $BB16_1 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: sync +; MIPS32R6O0-NEXT: move $2, $1 +; MIPS32R6O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: addiu $sp, $sp, 8 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: CheckSync: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(CheckSync))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(CheckSync))) +; MIPS4-NEXT: sync +; MIPS4-NEXT: ld $1, %got_disp(countsint)($1) +; MIPS4-NEXT: .LBB16_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $2, 0($1) +; MIPS4-NEXT: addu $3, $2, $4 +; MIPS4-NEXT: sc $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB16_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: sync +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: CheckSync: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(CheckSync))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(CheckSync))) +; MIPS64-NEXT: sync +; MIPS64-NEXT: ld $1, %got_disp(countsint)($1) +; MIPS64-NEXT: .LBB16_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $2, 0($1) +; MIPS64-NEXT: addu $3, $2, $4 +; MIPS64-NEXT: sc $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB16_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: sync +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: CheckSync: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(CheckSync))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(CheckSync))) +; MIPS64R2-NEXT: sync +; MIPS64R2-NEXT: ld $1, %got_disp(countsint)($1) +; MIPS64R2-NEXT: .LBB16_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $2, 0($1) +; MIPS64R2-NEXT: addu $3, $2, $4 +; MIPS64R2-NEXT: sc $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB16_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: sync +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: CheckSync: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(CheckSync))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(CheckSync))) +; MIPS64R6-NEXT: sync +; MIPS64R6-NEXT: ld $1, %got_disp(countsint)($1) +; MIPS64R6-NEXT: .LBB16_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $2, 0($1) +; MIPS64R6-NEXT: addu $3, $2, $4 +; MIPS64R6-NEXT: sc $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB16_1 +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: sync +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: CheckSync: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(CheckSync))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(CheckSync))) +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: sync +; MIPS64R6O0-NEXT: ld $1, %got_disp(countsint)($1) +; MIPS64R6O0-NEXT: .LBB16_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $3, 0($1) +; MIPS64R6O0-NEXT: addu $5, $3, $2 +; MIPS64R6O0-NEXT: sc $5, 0($1) +; MIPS64R6O0-NEXT: beqzc $5, .LBB16_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: sync +; MIPS64R6O0-NEXT: move $2, $3 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: CheckSync: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: sync +; MM32-NEXT: lw $1, %got(countsint)($2) +; MM32-NEXT: $BB16_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $2, 0($1) +; MM32-NEXT: addu16 $3, $2, $4 +; MM32-NEXT: sc $3, 0($1) +; MM32-NEXT: beqzc $3, $BB16_1 +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: sync +; MM32-NEXT: jrc $ra +; +; O1-LABEL: CheckSync: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: sync +; O1-NEXT: lw $1, %got(countsint)($1) +; O1-NEXT: $BB16_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $2, 0($1) +; O1-NEXT: addu $3, $2, $4 +; O1-NEXT: sc $3, 0($1) +; O1-NEXT: beqz $3, $BB16_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: sync +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: CheckSync: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: sync +; O2-NEXT: lw $1, %got(countsint)($1) +; O2-NEXT: $BB16_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $2, 0($1) +; O2-NEXT: addu $3, $2, $4 +; O2-NEXT: sc $3, 0($1) +; O2-NEXT: beqz $3, $BB16_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: sync +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: CheckSync: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: sync +; O3-NEXT: lw $1, %got(countsint)($1) +; O3-NEXT: $BB16_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $2, 0($1) +; O3-NEXT: addu $3, $2, $4 +; O3-NEXT: sc $3, 0($1) +; O3-NEXT: beqz $3, $BB16_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: sync +; O3-NEXT: jr $ra +; O3-NEXT: nop +; +; MIPS32EB-LABEL: CheckSync: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: sync +; MIPS32EB-NEXT: lw $1, %got(countsint)($1) +; MIPS32EB-NEXT: $BB16_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $2, 0($1) +; MIPS32EB-NEXT: addu $3, $2, $4 +; MIPS32EB-NEXT: sc $3, 0($1) +; MIPS32EB-NEXT: beqz $3, $BB16_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: sync +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: nop entry: %0 = atomicrmw add i32* @countsint, i32 %v seq_cst - ret i32 %0 - -; ALL-LABEL: CheckSync: - -; ALL: sync -; ALL: ll -; ALL: sc -; ALL: beq -; ALL: sync + ret i32 %0 } ; make sure that this assertion in ; TwoAddressInstructionPass::TryInstructionTransform does not fail: ; ; line 1203: assert(TargetRegisterInfo::isVirtualRegister(regB) && ; ; it failed when MipsDAGToDAGISel::ReplaceUsesWithZeroReg replaced an -; operand of an atomic instruction with register $zero. +; operand of an atomic instruction with register $zero. @a = external global i32 define i32 @zeroreg() nounwind { +; MIPS32-LABEL: zeroreg: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: sync +; MIPS32-NEXT: addiu $2, $zero, 0 +; MIPS32-NEXT: addiu $3, $zero, 1 +; MIPS32-NEXT: lw $1, %got(a)($1) +; MIPS32-NEXT: $BB17_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $4, 0($1) +; MIPS32-NEXT: bne $4, $3, $BB17_3 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: # in Loop: Header=BB17_1 Depth=1 +; MIPS32-NEXT: move $5, $2 +; MIPS32-NEXT: sc $5, 0($1) +; MIPS32-NEXT: beqz $5, $BB17_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: $BB17_3: # %entry +; MIPS32-NEXT: xor $1, $4, $3 +; MIPS32-NEXT: sltiu $2, $1, 1 +; MIPS32-NEXT: sync +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +; +; MIPS32O0-LABEL: zeroreg: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addiu $sp, $sp, -16 +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: sync +; MIPS32O0-NEXT: lw $2, %got(a)($2) +; MIPS32O0-NEXT: addiu $25, $zero, 0 +; MIPS32O0-NEXT: addiu $1, $zero, 1 +; MIPS32O0-NEXT: lw $3, 12($sp) # 4-byte Folded Reload +; MIPS32O0-NEXT: move $4, $1 +; MIPS32O0-NEXT: $BB17_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $5, 0($2) +; MIPS32O0-NEXT: bne $5, $4, $BB17_3 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: # in Loop: Header=BB17_1 Depth=1 +; MIPS32O0-NEXT: move $6, $25 +; MIPS32O0-NEXT: sc $6, 0($2) +; MIPS32O0-NEXT: beqz $6, $BB17_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: $BB17_3: # %entry +; MIPS32O0-NEXT: xor $1, $5, $1 +; MIPS32O0-NEXT: sltiu $1, $1, 1 +; MIPS32O0-NEXT: sync +; MIPS32O0-NEXT: addiu $2, $zero, 1 +; MIPS32O0-NEXT: xor $2, $5, $2 +; MIPS32O0-NEXT: sltiu $2, $2, 1 +; MIPS32O0-NEXT: andi $2, $2, 1 +; MIPS32O0-NEXT: sw $3, 8($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: sw $5, 12($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: sw $1, 4($sp) # 4-byte Folded Spill +; MIPS32O0-NEXT: addiu $sp, $sp, 16 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: zeroreg: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: sync +; MIPS32R2-NEXT: addiu $2, $zero, 0 +; MIPS32R2-NEXT: addiu $3, $zero, 1 +; MIPS32R2-NEXT: lw $1, %got(a)($1) +; MIPS32R2-NEXT: $BB17_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $4, 0($1) +; MIPS32R2-NEXT: bne $4, $3, $BB17_3 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: # in Loop: Header=BB17_1 Depth=1 +; MIPS32R2-NEXT: move $5, $2 +; MIPS32R2-NEXT: sc $5, 0($1) +; MIPS32R2-NEXT: beqz $5, $BB17_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: $BB17_3: # %entry +; MIPS32R2-NEXT: xor $1, $4, $3 +; MIPS32R2-NEXT: sltiu $2, $1, 1 +; MIPS32R2-NEXT: sync +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: nop +; +; MIPS32R6-LABEL: zeroreg: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: sync +; MIPS32R6-NEXT: addiu $2, $zero, 0 +; MIPS32R6-NEXT: addiu $3, $zero, 1 +; MIPS32R6-NEXT: lw $1, %got(a)($1) +; MIPS32R6-NEXT: $BB17_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $4, 0($1) +; MIPS32R6-NEXT: bnec $4, $3, $BB17_3 +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: # in Loop: Header=BB17_1 Depth=1 +; MIPS32R6-NEXT: move $5, $2 +; MIPS32R6-NEXT: sc $5, 0($1) +; MIPS32R6-NEXT: beqzc $5, $BB17_1 +; MIPS32R6-NEXT: $BB17_3: # %entry +; MIPS32R6-NEXT: xor $1, $4, $3 +; MIPS32R6-NEXT: sltiu $2, $1, 1 +; MIPS32R6-NEXT: sync +; MIPS32R6-NEXT: jrc $ra +; +; MIPS32R6O0-LABEL: zeroreg: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -8 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: sync +; MIPS32R6O0-NEXT: lw $2, %got(a)($2) +; MIPS32R6O0-NEXT: addiu $25, $zero, 0 +; MIPS32R6O0-NEXT: addiu $1, $zero, 1 +; MIPS32R6O0-NEXT: lw $3, 4($sp) # 4-byte Folded Reload +; MIPS32R6O0-NEXT: move $4, $1 +; MIPS32R6O0-NEXT: $BB17_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $5, 0($2) +; MIPS32R6O0-NEXT: bnec $5, $4, $BB17_3 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: # in Loop: Header=BB17_1 Depth=1 +; MIPS32R6O0-NEXT: move $6, $25 +; MIPS32R6O0-NEXT: sc $6, 0($2) +; MIPS32R6O0-NEXT: beqzc $6, $BB17_1 +; MIPS32R6O0-NEXT: $BB17_3: # %entry +; MIPS32R6O0-NEXT: xor $1, $5, $1 +; MIPS32R6O0-NEXT: sltiu $2, $1, 1 +; MIPS32R6O0-NEXT: sync +; MIPS32R6O0-NEXT: sw $3, 0($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: sw $5, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: addiu $sp, $sp, 8 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: zeroreg: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(zeroreg))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(zeroreg))) +; MIPS4-NEXT: sync +; MIPS4-NEXT: addiu $2, $zero, 0 +; MIPS4-NEXT: addiu $3, $zero, 1 +; MIPS4-NEXT: ld $1, %got_disp(a)($1) +; MIPS4-NEXT: .LBB17_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $4, 0($1) +; MIPS4-NEXT: bne $4, $3, .LBB17_3 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: # in Loop: Header=BB17_1 Depth=1 +; MIPS4-NEXT: move $5, $2 +; MIPS4-NEXT: sc $5, 0($1) +; MIPS4-NEXT: beqz $5, .LBB17_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: .LBB17_3: # %entry +; MIPS4-NEXT: xor $1, $4, $3 +; MIPS4-NEXT: sltiu $2, $1, 1 +; MIPS4-NEXT: sync +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: zeroreg: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(zeroreg))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(zeroreg))) +; MIPS64-NEXT: sync +; MIPS64-NEXT: addiu $2, $zero, 0 +; MIPS64-NEXT: addiu $3, $zero, 1 +; MIPS64-NEXT: ld $1, %got_disp(a)($1) +; MIPS64-NEXT: .LBB17_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $4, 0($1) +; MIPS64-NEXT: bne $4, $3, .LBB17_3 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: # in Loop: Header=BB17_1 Depth=1 +; MIPS64-NEXT: move $5, $2 +; MIPS64-NEXT: sc $5, 0($1) +; MIPS64-NEXT: beqz $5, .LBB17_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: .LBB17_3: # %entry +; MIPS64-NEXT: xor $1, $4, $3 +; MIPS64-NEXT: sltiu $2, $1, 1 +; MIPS64-NEXT: sync +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: zeroreg: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(zeroreg))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(zeroreg))) +; MIPS64R2-NEXT: sync +; MIPS64R2-NEXT: addiu $2, $zero, 0 +; MIPS64R2-NEXT: addiu $3, $zero, 1 +; MIPS64R2-NEXT: ld $1, %got_disp(a)($1) +; MIPS64R2-NEXT: .LBB17_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $4, 0($1) +; MIPS64R2-NEXT: bne $4, $3, .LBB17_3 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: # in Loop: Header=BB17_1 Depth=1 +; MIPS64R2-NEXT: move $5, $2 +; MIPS64R2-NEXT: sc $5, 0($1) +; MIPS64R2-NEXT: beqz $5, .LBB17_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: .LBB17_3: # %entry +; MIPS64R2-NEXT: xor $1, $4, $3 +; MIPS64R2-NEXT: sltiu $2, $1, 1 +; MIPS64R2-NEXT: sync +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: zeroreg: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(zeroreg))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(zeroreg))) +; MIPS64R6-NEXT: sync +; MIPS64R6-NEXT: addiu $2, $zero, 0 +; MIPS64R6-NEXT: addiu $3, $zero, 1 +; MIPS64R6-NEXT: ld $1, %got_disp(a)($1) +; MIPS64R6-NEXT: .LBB17_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $4, 0($1) +; MIPS64R6-NEXT: bnec $4, $3, .LBB17_3 +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: # in Loop: Header=BB17_1 Depth=1 +; MIPS64R6-NEXT: move $5, $2 +; MIPS64R6-NEXT: sc $5, 0($1) +; MIPS64R6-NEXT: beqzc $5, .LBB17_1 +; MIPS64R6-NEXT: .LBB17_3: # %entry +; MIPS64R6-NEXT: xor $1, $4, $3 +; MIPS64R6-NEXT: sltiu $2, $1, 1 +; MIPS64R6-NEXT: sync +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: zeroreg: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(zeroreg))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(zeroreg))) +; MIPS64R6O0-NEXT: sync +; MIPS64R6O0-NEXT: ld $1, %got_disp(a)($1) +; MIPS64R6O0-NEXT: addiu $2, $zero, 0 +; MIPS64R6O0-NEXT: addiu $3, $zero, 1 +; MIPS64R6O0-NEXT: lw $4, 12($sp) # 4-byte Folded Reload +; MIPS64R6O0-NEXT: move $5, $3 +; MIPS64R6O0-NEXT: .LBB17_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $6, 0($1) +; MIPS64R6O0-NEXT: bnec $6, $5, .LBB17_3 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: # in Loop: Header=BB17_1 Depth=1 +; MIPS64R6O0-NEXT: move $7, $2 +; MIPS64R6O0-NEXT: sc $7, 0($1) +; MIPS64R6O0-NEXT: beqzc $7, .LBB17_1 +; MIPS64R6O0-NEXT: .LBB17_3: # %entry +; MIPS64R6O0-NEXT: xor $2, $6, $3 +; MIPS64R6O0-NEXT: sltiu $2, $2, 1 +; MIPS64R6O0-NEXT: sync +; MIPS64R6O0-NEXT: sw $4, 8($sp) # 4-byte Folded Spill +; MIPS64R6O0-NEXT: sw $6, 12($sp) # 4-byte Folded Spill +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: zeroreg: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: sync +; MM32-NEXT: li16 $3, 0 +; MM32-NEXT: li16 $4, 1 +; MM32-NEXT: lw $1, %got(a)($2) +; MM32-NEXT: $BB17_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $2, 0($1) +; MM32-NEXT: bne $2, $4, $BB17_3 +; MM32-NEXT: nop +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: # in Loop: Header=BB17_1 Depth=1 +; MM32-NEXT: move $5, $3 +; MM32-NEXT: sc $5, 0($1) +; MM32-NEXT: beqzc $5, $BB17_1 +; MM32-NEXT: $BB17_3: # %entry +; MM32-NEXT: xor $1, $2, $4 +; MM32-NEXT: sltiu $2, $1, 1 +; MM32-NEXT: sync +; MM32-NEXT: jrc $ra +; +; O1-LABEL: zeroreg: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: sync +; O1-NEXT: addiu $2, $zero, 0 +; O1-NEXT: addiu $3, $zero, 1 +; O1-NEXT: lw $1, %got(a)($1) +; O1-NEXT: $BB17_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $4, 0($1) +; O1-NEXT: bne $4, $3, $BB17_3 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: # in Loop: Header=BB17_1 Depth=1 +; O1-NEXT: move $5, $2 +; O1-NEXT: sc $5, 0($1) +; O1-NEXT: beqz $5, $BB17_1 +; O1-NEXT: nop +; O1-NEXT: $BB17_3: # %entry +; O1-NEXT: xor $1, $4, $3 +; O1-NEXT: sltiu $2, $1, 1 +; O1-NEXT: sync +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: zeroreg: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: sync +; O2-NEXT: addiu $2, $zero, 0 +; O2-NEXT: addiu $3, $zero, 1 +; O2-NEXT: lw $1, %got(a)($1) +; O2-NEXT: $BB17_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $4, 0($1) +; O2-NEXT: bne $4, $3, $BB17_3 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: # in Loop: Header=BB17_1 Depth=1 +; O2-NEXT: move $5, $2 +; O2-NEXT: sc $5, 0($1) +; O2-NEXT: beqz $5, $BB17_1 +; O2-NEXT: nop +; O2-NEXT: $BB17_3: # %entry +; O2-NEXT: xor $1, $4, $3 +; O2-NEXT: sltiu $2, $1, 1 +; O2-NEXT: sync +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: zeroreg: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: addiu $2, $zero, 0 +; O3-NEXT: addiu $3, $zero, 1 +; O3-NEXT: sync +; O3-NEXT: lw $1, %got(a)($1) +; O3-NEXT: $BB17_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $4, 0($1) +; O3-NEXT: bne $4, $3, $BB17_3 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: # in Loop: Header=BB17_1 Depth=1 +; O3-NEXT: move $5, $2 +; O3-NEXT: sc $5, 0($1) +; O3-NEXT: beqz $5, $BB17_1 +; O3-NEXT: nop +; O3-NEXT: $BB17_3: # %entry +; O3-NEXT: sync +; O3-NEXT: xor $1, $4, $3 +; O3-NEXT: jr $ra +; O3-NEXT: sltiu $2, $1, 1 +; +; MIPS32EB-LABEL: zeroreg: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: sync +; MIPS32EB-NEXT: addiu $2, $zero, 0 +; MIPS32EB-NEXT: addiu $3, $zero, 1 +; MIPS32EB-NEXT: lw $1, %got(a)($1) +; MIPS32EB-NEXT: $BB17_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $4, 0($1) +; MIPS32EB-NEXT: bne $4, $3, $BB17_3 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: # in Loop: Header=BB17_1 Depth=1 +; MIPS32EB-NEXT: move $5, $2 +; MIPS32EB-NEXT: sc $5, 0($1) +; MIPS32EB-NEXT: beqz $5, $BB17_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: $BB17_3: # %entry +; MIPS32EB-NEXT: xor $1, $4, $3 +; MIPS32EB-NEXT: sltiu $2, $1, 1 +; MIPS32EB-NEXT: sync +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: nop entry: %pair0 = cmpxchg i32* @a, i32 1, i32 0 seq_cst seq_cst %0 = extractvalue { i32, i1 } %pair0, 0 %1 = icmp eq i32 %0, 1 %conv = zext i1 %1 to i32 ret i32 %conv } ; Check that MIPS32R6 has the correct offset range. ; FIXME: At the moment, we don't seem to do addr+offset for any atomic load/store. define i32 @AtomicLoadAdd32_OffGt9Bit(i32 signext %incr) nounwind { +; MIPS32-LABEL: AtomicLoadAdd32_OffGt9Bit: +; MIPS32: # %bb.0: # %entry +; MIPS32-NEXT: lui $2, %hi(_gp_disp) +; MIPS32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32-NEXT: addu $1, $2, $25 +; MIPS32-NEXT: lw $1, %got(x)($1) +; MIPS32-NEXT: addiu $1, $1, 1024 +; MIPS32-NEXT: $BB18_1: # %entry +; MIPS32-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32-NEXT: ll $2, 0($1) +; MIPS32-NEXT: addu $3, $2, $4 +; MIPS32-NEXT: sc $3, 0($1) +; MIPS32-NEXT: beqz $3, $BB18_1 +; MIPS32-NEXT: nop +; MIPS32-NEXT: # %bb.2: # %entry +; MIPS32-NEXT: jr $ra +; MIPS32-NEXT: nop +; +; MIPS32O0-LABEL: AtomicLoadAdd32_OffGt9Bit: +; MIPS32O0: # %bb.0: # %entry +; MIPS32O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32O0-NEXT: addu $2, $2, $25 +; MIPS32O0-NEXT: lw $2, %got(x)($2) +; MIPS32O0-NEXT: addiu $2, $2, 1024 +; MIPS32O0-NEXT: $BB18_1: # %entry +; MIPS32O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32O0-NEXT: ll $25, 0($2) +; MIPS32O0-NEXT: addu $1, $25, $4 +; MIPS32O0-NEXT: sc $1, 0($2) +; MIPS32O0-NEXT: beqz $1, $BB18_1 +; MIPS32O0-NEXT: nop +; MIPS32O0-NEXT: # %bb.2: # %entry +; MIPS32O0-NEXT: move $2, $25 +; MIPS32O0-NEXT: jr $ra +; MIPS32O0-NEXT: nop +; +; MIPS32R2-LABEL: AtomicLoadAdd32_OffGt9Bit: +; MIPS32R2: # %bb.0: # %entry +; MIPS32R2-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R2-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R2-NEXT: addu $1, $2, $25 +; MIPS32R2-NEXT: lw $1, %got(x)($1) +; MIPS32R2-NEXT: addiu $1, $1, 1024 +; MIPS32R2-NEXT: $BB18_1: # %entry +; MIPS32R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R2-NEXT: ll $2, 0($1) +; MIPS32R2-NEXT: addu $3, $2, $4 +; MIPS32R2-NEXT: sc $3, 0($1) +; MIPS32R2-NEXT: beqz $3, $BB18_1 +; MIPS32R2-NEXT: nop +; MIPS32R2-NEXT: # %bb.2: # %entry +; MIPS32R2-NEXT: jr $ra +; MIPS32R2-NEXT: nop +; +; MIPS32R6-LABEL: AtomicLoadAdd32_OffGt9Bit: +; MIPS32R6: # %bb.0: # %entry +; MIPS32R6-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6-NEXT: addu $1, $2, $25 +; MIPS32R6-NEXT: lw $1, %got(x)($1) +; MIPS32R6-NEXT: addiu $1, $1, 1024 +; MIPS32R6-NEXT: $BB18_1: # %entry +; MIPS32R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6-NEXT: ll $2, 0($1) +; MIPS32R6-NEXT: addu $3, $2, $4 +; MIPS32R6-NEXT: sc $3, 0($1) +; MIPS32R6-NEXT: beqzc $3, $BB18_1 +; MIPS32R6-NEXT: nop +; MIPS32R6-NEXT: # %bb.2: # %entry +; MIPS32R6-NEXT: jrc $ra +; +; MIPS32R6O0-LABEL: AtomicLoadAdd32_OffGt9Bit: +; MIPS32R6O0: # %bb.0: # %entry +; MIPS32R6O0-NEXT: lui $2, %hi(_gp_disp) +; MIPS32R6O0-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32R6O0-NEXT: addiu $sp, $sp, -8 +; MIPS32R6O0-NEXT: addu $2, $2, $25 +; MIPS32R6O0-NEXT: move $25, $4 +; MIPS32R6O0-NEXT: lw $2, %got(x)($2) +; MIPS32R6O0-NEXT: addiu $2, $2, 1024 +; MIPS32R6O0-NEXT: $BB18_1: # %entry +; MIPS32R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32R6O0-NEXT: ll $1, 0($2) +; MIPS32R6O0-NEXT: addu $3, $1, $4 +; MIPS32R6O0-NEXT: sc $3, 0($2) +; MIPS32R6O0-NEXT: beqzc $3, $BB18_1 +; MIPS32R6O0-NEXT: # %bb.2: # %entry +; MIPS32R6O0-NEXT: move $2, $1 +; MIPS32R6O0-NEXT: sw $25, 4($sp) # 4-byte Folded Spill +; MIPS32R6O0-NEXT: addiu $sp, $sp, 8 +; MIPS32R6O0-NEXT: jrc $ra +; +; MIPS4-LABEL: AtomicLoadAdd32_OffGt9Bit: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd32_OffGt9Bit))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd32_OffGt9Bit))) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: daddiu $1, $1, 1024 +; MIPS4-NEXT: .LBB18_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: ll $2, 0($1) +; MIPS4-NEXT: addu $3, $2, $4 +; MIPS4-NEXT: sc $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB18_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: AtomicLoadAdd32_OffGt9Bit: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd32_OffGt9Bit))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd32_OffGt9Bit))) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: daddiu $1, $1, 1024 +; MIPS64-NEXT: .LBB18_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: ll $2, 0($1) +; MIPS64-NEXT: addu $3, $2, $4 +; MIPS64-NEXT: sc $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB18_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: AtomicLoadAdd32_OffGt9Bit: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd32_OffGt9Bit))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd32_OffGt9Bit))) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: daddiu $1, $1, 1024 +; MIPS64R2-NEXT: .LBB18_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: ll $2, 0($1) +; MIPS64R2-NEXT: addu $3, $2, $4 +; MIPS64R2-NEXT: sc $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB18_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: AtomicLoadAdd32_OffGt9Bit: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd32_OffGt9Bit))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd32_OffGt9Bit))) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: daddiu $1, $1, 1024 +; MIPS64R6-NEXT: .LBB18_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: ll $2, 0($1) +; MIPS64R6-NEXT: addu $3, $2, $4 +; MIPS64R6-NEXT: sc $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB18_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: AtomicLoadAdd32_OffGt9Bit: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd32_OffGt9Bit))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd32_OffGt9Bit))) +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: daddiu $1, $1, 1024 +; MIPS64R6O0-NEXT: .LBB18_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: ll $3, 0($1) +; MIPS64R6O0-NEXT: addu $5, $3, $2 +; MIPS64R6O0-NEXT: sc $5, 0($1) +; MIPS64R6O0-NEXT: beqzc $5, .LBB18_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: move $2, $3 +; MIPS64R6O0-NEXT: jrc $ra +; +; MM32-LABEL: AtomicLoadAdd32_OffGt9Bit: +; MM32: # %bb.0: # %entry +; MM32-NEXT: lui $2, %hi(_gp_disp) +; MM32-NEXT: addiu $2, $2, %lo(_gp_disp) +; MM32-NEXT: addu $2, $2, $25 +; MM32-NEXT: lw $1, %got(x)($2) +; MM32-NEXT: addiu $1, $1, 1024 +; MM32-NEXT: $BB18_1: # %entry +; MM32-NEXT: # =>This Inner Loop Header: Depth=1 +; MM32-NEXT: ll $2, 0($1) +; MM32-NEXT: addu16 $3, $2, $4 +; MM32-NEXT: sc $3, 0($1) +; MM32-NEXT: beqzc $3, $BB18_1 +; MM32-NEXT: # %bb.2: # %entry +; MM32-NEXT: jrc $ra +; +; O1-LABEL: AtomicLoadAdd32_OffGt9Bit: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $2, %hi(_gp_disp) +; O1-NEXT: addiu $2, $2, %lo(_gp_disp) +; O1-NEXT: addu $1, $2, $25 +; O1-NEXT: lw $1, %got(x)($1) +; O1-NEXT: addiu $1, $1, 1024 +; O1-NEXT: $BB18_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: ll $2, 0($1) +; O1-NEXT: addu $3, $2, $4 +; O1-NEXT: sc $3, 0($1) +; O1-NEXT: beqz $3, $BB18_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: AtomicLoadAdd32_OffGt9Bit: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $2, %hi(_gp_disp) +; O2-NEXT: addiu $2, $2, %lo(_gp_disp) +; O2-NEXT: addu $1, $2, $25 +; O2-NEXT: lw $1, %got(x)($1) +; O2-NEXT: addiu $1, $1, 1024 +; O2-NEXT: $BB18_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: ll $2, 0($1) +; O2-NEXT: addu $3, $2, $4 +; O2-NEXT: sc $3, 0($1) +; O2-NEXT: beqz $3, $BB18_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: AtomicLoadAdd32_OffGt9Bit: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $2, %hi(_gp_disp) +; O3-NEXT: addiu $2, $2, %lo(_gp_disp) +; O3-NEXT: addu $1, $2, $25 +; O3-NEXT: lw $1, %got(x)($1) +; O3-NEXT: addiu $1, $1, 1024 +; O3-NEXT: $BB18_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: ll $2, 0($1) +; O3-NEXT: addu $3, $2, $4 +; O3-NEXT: sc $3, 0($1) +; O3-NEXT: beqz $3, $BB18_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: nop +; +; MIPS32EB-LABEL: AtomicLoadAdd32_OffGt9Bit: +; MIPS32EB: # %bb.0: # %entry +; MIPS32EB-NEXT: lui $2, %hi(_gp_disp) +; MIPS32EB-NEXT: addiu $2, $2, %lo(_gp_disp) +; MIPS32EB-NEXT: addu $1, $2, $25 +; MIPS32EB-NEXT: lw $1, %got(x)($1) +; MIPS32EB-NEXT: addiu $1, $1, 1024 +; MIPS32EB-NEXT: $BB18_1: # %entry +; MIPS32EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS32EB-NEXT: ll $2, 0($1) +; MIPS32EB-NEXT: addu $3, $2, $4 +; MIPS32EB-NEXT: sc $3, 0($1) +; MIPS32EB-NEXT: beqz $3, $BB18_1 +; MIPS32EB-NEXT: nop +; MIPS32EB-NEXT: # %bb.2: # %entry +; MIPS32EB-NEXT: jr $ra +; MIPS32EB-NEXT: nop entry: %0 = atomicrmw add i32* getelementptr(i32, i32* @x, i32 256), i32 %incr monotonic ret i32 %0 -; ALL-LABEL: AtomicLoadAdd32_OffGt9Bit: - -; MIPS32-ANY: lw $[[R0:[0-9]+]], %got(x) -; MIPS64-ANY: ld $[[R0:[0-9]+]], %got_disp(x)( - -; ALL: addiu $[[PTR:[0-9]+]], $[[R0]], 1024 -; ALL: [[BB0:(\$|\.L)[A-Z_0-9]+]]: -; ALL: ll $[[R1:[0-9]+]], 0($[[PTR]]) -; ALL: addu $[[R2:[0-9]+]], $[[R1]], $4 -; ALL: sc $[[R2]], 0($[[PTR]]) -; NOT-MICROMIPS: beqz $[[R2]], [[BB0]] -; MICROMIPS: beqzc $[[R2]], [[BB0]] -; MIPSR6: beqzc $[[R2]], [[BB0]] } Index: llvm/trunk/test/CodeGen/Mips/micromips-atomic.ll =================================================================== --- llvm/trunk/test/CodeGen/Mips/micromips-atomic.ll (revision 336327) +++ llvm/trunk/test/CodeGen/Mips/micromips-atomic.ll (revision 336328) @@ -1,18 +1,25 @@ -; RUN: llc %s -march=mipsel -mcpu=mips32r2 -mattr=micromips -filetype=asm \ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc %s -mtriple=mipsel-unknown-linux-gnu -mcpu=mips32r2 -mattr=micromips -filetype=asm \ ; RUN: -relocation-model=pic -o - | FileCheck %s @x = common global i32 0, align 4 define i32 @AtomicLoadAdd32(i32 %incr) nounwind { +; CHECK-LABEL: AtomicLoadAdd32: +; CHECK: # %bb.0: # %entry +; CHECK-NEXT: lui $2, %hi(_gp_disp) +; CHECK-NEXT: addiu $2, $2, %lo(_gp_disp) +; CHECK-NEXT: addu $2, $2, $25 +; CHECK-NEXT: lw $1, %got(x)($2) +; CHECK-NEXT: $BB0_1: # %entry +; CHECK-NEXT: # =>This Inner Loop Header: Depth=1 +; CHECK-NEXT: ll $2, 0($1) +; CHECK-NEXT: addu16 $3, $2, $4 +; CHECK-NEXT: sc $3, 0($1) +; CHECK-NEXT: beqzc $3, $BB0_1 +; CHECK-NEXT: # %bb.2: # %entry +; CHECK-NEXT: jrc $ra entry: %0 = atomicrmw add i32* @x, i32 %incr monotonic ret i32 %0 - -; CHECK-LABEL: AtomicLoadAdd32: -; CHECK: lw $[[R0:[0-9]+]], %got(x) -; CHECK: $[[BB0:[A-Z_0-9]+]]: -; CHECK: ll $[[R1:[0-9]+]], 0($[[R0]]) -; CHECK: addu $[[R2:[0-9]+]], $[[R1]], $4 -; CHECK: sc $[[R2]], 0($[[R0]]) -; CHECK: beqzc $[[R2]], $[[BB0]] } Index: llvm/trunk/test/CodeGen/Mips/atomic64.ll =================================================================== --- llvm/trunk/test/CodeGen/Mips/atomic64.ll (revision 0) +++ llvm/trunk/test/CodeGen/Mips/atomic64.ll (revision 336328) @@ -0,0 +1,1397 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; RUN: llc -mtriple=mips64el-unknown-linux-gnu --disable-machine-licm -mcpu=mips4 -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS4 +; RUN: llc -mtriple=mips64el-unknown-linux-gnu --disable-machine-licm -mcpu=mips64 -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS64 +; RUN: llc -mtriple=mips64el-unknown-linux-gnu --disable-machine-licm -mcpu=mips64r2 -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS64R2 +; RUN: llc -mtriple=mips64el-unknown-linux-gnu --disable-machine-licm -mcpu=mips64r6 -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS64R6 +; RUN: llc -mtriple=mips64-unknown-linux-gnu -O0 -mcpu=mips64r6 -relocation-model=pic -verify-machineinstrs -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS64R6O0 + +; We want to verify the produced code is well formed all optimization levels, the rest of the test which ensure correctness. +; RUN: llc -mtriple=mips64el-unknown-linux-gnu -O1 --disable-machine-licm -mcpu=mips64 -relocation-model=pic -verify-machineinstrs < %s | FileCheck %s --check-prefix=O1 +; RUN: llc -mtriple=mips64el-unknown-linux-gnu -O2 --disable-machine-licm -mcpu=mips64 -relocation-model=pic -verify-machineinstrs < %s | FileCheck %s --check-prefix=O2 +; RUN: llc -mtriple=mips64el-unknown-linux-gnu -O3 --disable-machine-licm -mcpu=mips64 -relocation-model=pic -verify-machineinstrs < %s | FileCheck %s --check-prefix=O3 + +; Keep one big-endian check so that we don't reduce testing, but don't add more +; since endianness doesn't affect the body of the atomic operations. +; RUN: llc -mtriple=mips64-unknown-linux-gnu --disable-machine-licm -mcpu=mips64 -relocation-model=pic -verify-machineinstrs < %s | \ +; RUN: FileCheck %s -check-prefix=MIPS64EB + +@x = common global i64 0, align 4 + +define i64 @AtomicLoadAdd(i64 signext %incr) nounwind { +; MIPS4-LABEL: AtomicLoadAdd: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd))) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB0_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: lld $2, 0($1) +; MIPS4-NEXT: daddu $3, $2, $4 +; MIPS4-NEXT: scd $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB0_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: AtomicLoadAdd: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd))) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB0_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: lld $2, 0($1) +; MIPS64-NEXT: daddu $3, $2, $4 +; MIPS64-NEXT: scd $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB0_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: AtomicLoadAdd: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd))) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB0_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: lld $2, 0($1) +; MIPS64R2-NEXT: daddu $3, $2, $4 +; MIPS64R2-NEXT: scd $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB0_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: AtomicLoadAdd: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd))) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB0_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: lld $2, 0($1) +; MIPS64R6-NEXT: daddu $3, $2, $4 +; MIPS64R6-NEXT: scd $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB0_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: AtomicLoadAdd: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd))) +; MIPS64R6O0-NEXT: move $25, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: .LBB0_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: lld $2, 0($1) +; MIPS64R6O0-NEXT: daddu $3, $2, $4 +; MIPS64R6O0-NEXT: scd $3, 0($1) +; MIPS64R6O0-NEXT: beqzc $3, .LBB0_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: sd $25, 8($sp) # 8-byte Folded Spill +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; O1-LABEL: AtomicLoadAdd: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd))) +; O1-NEXT: daddu $1, $1, $25 +; O1-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd))) +; O1-NEXT: ld $1, %got_disp(x)($1) +; O1-NEXT: .LBB0_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: lld $2, 0($1) +; O1-NEXT: daddu $3, $2, $4 +; O1-NEXT: scd $3, 0($1) +; O1-NEXT: beqz $3, .LBB0_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: AtomicLoadAdd: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd))) +; O2-NEXT: daddu $1, $1, $25 +; O2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd))) +; O2-NEXT: ld $1, %got_disp(x)($1) +; O2-NEXT: .LBB0_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: lld $2, 0($1) +; O2-NEXT: daddu $3, $2, $4 +; O2-NEXT: scd $3, 0($1) +; O2-NEXT: beqz $3, .LBB0_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: AtomicLoadAdd: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd))) +; O3-NEXT: daddu $1, $1, $25 +; O3-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd))) +; O3-NEXT: ld $1, %got_disp(x)($1) +; O3-NEXT: .LBB0_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: lld $2, 0($1) +; O3-NEXT: daddu $3, $2, $4 +; O3-NEXT: scd $3, 0($1) +; O3-NEXT: beqz $3, .LBB0_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: nop +; +; MIPS64EB-LABEL: AtomicLoadAdd: +; MIPS64EB: # %bb.0: # %entry +; MIPS64EB-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAdd))) +; MIPS64EB-NEXT: daddu $1, $1, $25 +; MIPS64EB-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAdd))) +; MIPS64EB-NEXT: ld $1, %got_disp(x)($1) +; MIPS64EB-NEXT: .LBB0_1: # %entry +; MIPS64EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64EB-NEXT: lld $2, 0($1) +; MIPS64EB-NEXT: daddu $3, $2, $4 +; MIPS64EB-NEXT: scd $3, 0($1) +; MIPS64EB-NEXT: beqz $3, .LBB0_1 +; MIPS64EB-NEXT: nop +; MIPS64EB-NEXT: # %bb.2: # %entry +; MIPS64EB-NEXT: jr $ra +; MIPS64EB-NEXT: nop +entry: + %0 = atomicrmw add i64* @x, i64 %incr monotonic + ret i64 %0 + +} + +define i64 @AtomicLoadSub(i64 signext %incr) nounwind { +; MIPS4-LABEL: AtomicLoadSub: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub))) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB1_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: lld $2, 0($1) +; MIPS4-NEXT: dsubu $3, $2, $4 +; MIPS4-NEXT: scd $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB1_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: AtomicLoadSub: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub))) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB1_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: lld $2, 0($1) +; MIPS64-NEXT: dsubu $3, $2, $4 +; MIPS64-NEXT: scd $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB1_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: AtomicLoadSub: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub))) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB1_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: lld $2, 0($1) +; MIPS64R2-NEXT: dsubu $3, $2, $4 +; MIPS64R2-NEXT: scd $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB1_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: AtomicLoadSub: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub))) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB1_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: lld $2, 0($1) +; MIPS64R6-NEXT: dsubu $3, $2, $4 +; MIPS64R6-NEXT: scd $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB1_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: AtomicLoadSub: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub))) +; MIPS64R6O0-NEXT: move $25, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: .LBB1_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: lld $2, 0($1) +; MIPS64R6O0-NEXT: dsubu $3, $2, $4 +; MIPS64R6O0-NEXT: scd $3, 0($1) +; MIPS64R6O0-NEXT: beqzc $3, .LBB1_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: sd $25, 8($sp) # 8-byte Folded Spill +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; O1-LABEL: AtomicLoadSub: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub))) +; O1-NEXT: daddu $1, $1, $25 +; O1-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub))) +; O1-NEXT: ld $1, %got_disp(x)($1) +; O1-NEXT: .LBB1_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: lld $2, 0($1) +; O1-NEXT: dsubu $3, $2, $4 +; O1-NEXT: scd $3, 0($1) +; O1-NEXT: beqz $3, .LBB1_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: AtomicLoadSub: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub))) +; O2-NEXT: daddu $1, $1, $25 +; O2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub))) +; O2-NEXT: ld $1, %got_disp(x)($1) +; O2-NEXT: .LBB1_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: lld $2, 0($1) +; O2-NEXT: dsubu $3, $2, $4 +; O2-NEXT: scd $3, 0($1) +; O2-NEXT: beqz $3, .LBB1_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: AtomicLoadSub: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub))) +; O3-NEXT: daddu $1, $1, $25 +; O3-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub))) +; O3-NEXT: ld $1, %got_disp(x)($1) +; O3-NEXT: .LBB1_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: lld $2, 0($1) +; O3-NEXT: dsubu $3, $2, $4 +; O3-NEXT: scd $3, 0($1) +; O3-NEXT: beqz $3, .LBB1_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: nop +; +; MIPS64EB-LABEL: AtomicLoadSub: +; MIPS64EB: # %bb.0: # %entry +; MIPS64EB-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadSub))) +; MIPS64EB-NEXT: daddu $1, $1, $25 +; MIPS64EB-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadSub))) +; MIPS64EB-NEXT: ld $1, %got_disp(x)($1) +; MIPS64EB-NEXT: .LBB1_1: # %entry +; MIPS64EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64EB-NEXT: lld $2, 0($1) +; MIPS64EB-NEXT: dsubu $3, $2, $4 +; MIPS64EB-NEXT: scd $3, 0($1) +; MIPS64EB-NEXT: beqz $3, .LBB1_1 +; MIPS64EB-NEXT: nop +; MIPS64EB-NEXT: # %bb.2: # %entry +; MIPS64EB-NEXT: jr $ra +; MIPS64EB-NEXT: nop +entry: + %0 = atomicrmw sub i64* @x, i64 %incr monotonic + ret i64 %0 + +} + +define i64 @AtomicLoadAnd(i64 signext %incr) nounwind { +; MIPS4-LABEL: AtomicLoadAnd: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAnd))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAnd))) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB2_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: lld $2, 0($1) +; MIPS4-NEXT: and $3, $2, $4 +; MIPS4-NEXT: scd $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB2_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: AtomicLoadAnd: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAnd))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAnd))) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB2_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: lld $2, 0($1) +; MIPS64-NEXT: and $3, $2, $4 +; MIPS64-NEXT: scd $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB2_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: AtomicLoadAnd: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAnd))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAnd))) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB2_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: lld $2, 0($1) +; MIPS64R2-NEXT: and $3, $2, $4 +; MIPS64R2-NEXT: scd $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB2_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: AtomicLoadAnd: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAnd))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAnd))) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB2_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: lld $2, 0($1) +; MIPS64R6-NEXT: and $3, $2, $4 +; MIPS64R6-NEXT: scd $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB2_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: AtomicLoadAnd: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAnd))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAnd))) +; MIPS64R6O0-NEXT: move $25, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: .LBB2_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: lld $2, 0($1) +; MIPS64R6O0-NEXT: and $3, $2, $4 +; MIPS64R6O0-NEXT: scd $3, 0($1) +; MIPS64R6O0-NEXT: beqzc $3, .LBB2_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: sd $25, 8($sp) # 8-byte Folded Spill +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; O1-LABEL: AtomicLoadAnd: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAnd))) +; O1-NEXT: daddu $1, $1, $25 +; O1-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAnd))) +; O1-NEXT: ld $1, %got_disp(x)($1) +; O1-NEXT: .LBB2_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: lld $2, 0($1) +; O1-NEXT: and $3, $2, $4 +; O1-NEXT: scd $3, 0($1) +; O1-NEXT: beqz $3, .LBB2_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: AtomicLoadAnd: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAnd))) +; O2-NEXT: daddu $1, $1, $25 +; O2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAnd))) +; O2-NEXT: ld $1, %got_disp(x)($1) +; O2-NEXT: .LBB2_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: lld $2, 0($1) +; O2-NEXT: and $3, $2, $4 +; O2-NEXT: scd $3, 0($1) +; O2-NEXT: beqz $3, .LBB2_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: AtomicLoadAnd: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAnd))) +; O3-NEXT: daddu $1, $1, $25 +; O3-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAnd))) +; O3-NEXT: ld $1, %got_disp(x)($1) +; O3-NEXT: .LBB2_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: lld $2, 0($1) +; O3-NEXT: and $3, $2, $4 +; O3-NEXT: scd $3, 0($1) +; O3-NEXT: beqz $3, .LBB2_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: nop +; +; MIPS64EB-LABEL: AtomicLoadAnd: +; MIPS64EB: # %bb.0: # %entry +; MIPS64EB-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadAnd))) +; MIPS64EB-NEXT: daddu $1, $1, $25 +; MIPS64EB-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadAnd))) +; MIPS64EB-NEXT: ld $1, %got_disp(x)($1) +; MIPS64EB-NEXT: .LBB2_1: # %entry +; MIPS64EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64EB-NEXT: lld $2, 0($1) +; MIPS64EB-NEXT: and $3, $2, $4 +; MIPS64EB-NEXT: scd $3, 0($1) +; MIPS64EB-NEXT: beqz $3, .LBB2_1 +; MIPS64EB-NEXT: nop +; MIPS64EB-NEXT: # %bb.2: # %entry +; MIPS64EB-NEXT: jr $ra +; MIPS64EB-NEXT: nop +entry: + %0 = atomicrmw and i64* @x, i64 %incr monotonic + ret i64 %0 + +} + +define i64 @AtomicLoadOr(i64 signext %incr) nounwind { +; MIPS4-LABEL: AtomicLoadOr: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadOr))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadOr))) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB3_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: lld $2, 0($1) +; MIPS4-NEXT: or $3, $2, $4 +; MIPS4-NEXT: scd $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB3_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: AtomicLoadOr: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadOr))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadOr))) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB3_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: lld $2, 0($1) +; MIPS64-NEXT: or $3, $2, $4 +; MIPS64-NEXT: scd $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB3_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: AtomicLoadOr: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadOr))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadOr))) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB3_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: lld $2, 0($1) +; MIPS64R2-NEXT: or $3, $2, $4 +; MIPS64R2-NEXT: scd $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB3_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: AtomicLoadOr: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadOr))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadOr))) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB3_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: lld $2, 0($1) +; MIPS64R6-NEXT: or $3, $2, $4 +; MIPS64R6-NEXT: scd $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB3_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: AtomicLoadOr: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadOr))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadOr))) +; MIPS64R6O0-NEXT: move $25, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: .LBB3_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: lld $2, 0($1) +; MIPS64R6O0-NEXT: or $3, $2, $4 +; MIPS64R6O0-NEXT: scd $3, 0($1) +; MIPS64R6O0-NEXT: beqzc $3, .LBB3_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: sd $25, 8($sp) # 8-byte Folded Spill +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; O1-LABEL: AtomicLoadOr: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadOr))) +; O1-NEXT: daddu $1, $1, $25 +; O1-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadOr))) +; O1-NEXT: ld $1, %got_disp(x)($1) +; O1-NEXT: .LBB3_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: lld $2, 0($1) +; O1-NEXT: or $3, $2, $4 +; O1-NEXT: scd $3, 0($1) +; O1-NEXT: beqz $3, .LBB3_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: AtomicLoadOr: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadOr))) +; O2-NEXT: daddu $1, $1, $25 +; O2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadOr))) +; O2-NEXT: ld $1, %got_disp(x)($1) +; O2-NEXT: .LBB3_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: lld $2, 0($1) +; O2-NEXT: or $3, $2, $4 +; O2-NEXT: scd $3, 0($1) +; O2-NEXT: beqz $3, .LBB3_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: AtomicLoadOr: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadOr))) +; O3-NEXT: daddu $1, $1, $25 +; O3-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadOr))) +; O3-NEXT: ld $1, %got_disp(x)($1) +; O3-NEXT: .LBB3_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: lld $2, 0($1) +; O3-NEXT: or $3, $2, $4 +; O3-NEXT: scd $3, 0($1) +; O3-NEXT: beqz $3, .LBB3_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: nop +; +; MIPS64EB-LABEL: AtomicLoadOr: +; MIPS64EB: # %bb.0: # %entry +; MIPS64EB-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadOr))) +; MIPS64EB-NEXT: daddu $1, $1, $25 +; MIPS64EB-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadOr))) +; MIPS64EB-NEXT: ld $1, %got_disp(x)($1) +; MIPS64EB-NEXT: .LBB3_1: # %entry +; MIPS64EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64EB-NEXT: lld $2, 0($1) +; MIPS64EB-NEXT: or $3, $2, $4 +; MIPS64EB-NEXT: scd $3, 0($1) +; MIPS64EB-NEXT: beqz $3, .LBB3_1 +; MIPS64EB-NEXT: nop +; MIPS64EB-NEXT: # %bb.2: # %entry +; MIPS64EB-NEXT: jr $ra +; MIPS64EB-NEXT: nop +entry: + %0 = atomicrmw or i64* @x, i64 %incr monotonic + ret i64 %0 + +} + +define i64 @AtomicLoadXor(i64 signext %incr) nounwind { +; MIPS4-LABEL: AtomicLoadXor: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadXor))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadXor))) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB4_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: lld $2, 0($1) +; MIPS4-NEXT: xor $3, $2, $4 +; MIPS4-NEXT: scd $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB4_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: AtomicLoadXor: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadXor))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadXor))) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB4_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: lld $2, 0($1) +; MIPS64-NEXT: xor $3, $2, $4 +; MIPS64-NEXT: scd $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB4_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: AtomicLoadXor: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadXor))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadXor))) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB4_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: lld $2, 0($1) +; MIPS64R2-NEXT: xor $3, $2, $4 +; MIPS64R2-NEXT: scd $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB4_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: AtomicLoadXor: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadXor))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadXor))) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB4_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: lld $2, 0($1) +; MIPS64R6-NEXT: xor $3, $2, $4 +; MIPS64R6-NEXT: scd $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB4_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: AtomicLoadXor: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadXor))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadXor))) +; MIPS64R6O0-NEXT: move $25, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: .LBB4_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: lld $2, 0($1) +; MIPS64R6O0-NEXT: xor $3, $2, $4 +; MIPS64R6O0-NEXT: scd $3, 0($1) +; MIPS64R6O0-NEXT: beqzc $3, .LBB4_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: sd $25, 8($sp) # 8-byte Folded Spill +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; O1-LABEL: AtomicLoadXor: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadXor))) +; O1-NEXT: daddu $1, $1, $25 +; O1-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadXor))) +; O1-NEXT: ld $1, %got_disp(x)($1) +; O1-NEXT: .LBB4_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: lld $2, 0($1) +; O1-NEXT: xor $3, $2, $4 +; O1-NEXT: scd $3, 0($1) +; O1-NEXT: beqz $3, .LBB4_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: AtomicLoadXor: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadXor))) +; O2-NEXT: daddu $1, $1, $25 +; O2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadXor))) +; O2-NEXT: ld $1, %got_disp(x)($1) +; O2-NEXT: .LBB4_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: lld $2, 0($1) +; O2-NEXT: xor $3, $2, $4 +; O2-NEXT: scd $3, 0($1) +; O2-NEXT: beqz $3, .LBB4_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: AtomicLoadXor: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadXor))) +; O3-NEXT: daddu $1, $1, $25 +; O3-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadXor))) +; O3-NEXT: ld $1, %got_disp(x)($1) +; O3-NEXT: .LBB4_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: lld $2, 0($1) +; O3-NEXT: xor $3, $2, $4 +; O3-NEXT: scd $3, 0($1) +; O3-NEXT: beqz $3, .LBB4_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: nop +; +; MIPS64EB-LABEL: AtomicLoadXor: +; MIPS64EB: # %bb.0: # %entry +; MIPS64EB-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadXor))) +; MIPS64EB-NEXT: daddu $1, $1, $25 +; MIPS64EB-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadXor))) +; MIPS64EB-NEXT: ld $1, %got_disp(x)($1) +; MIPS64EB-NEXT: .LBB4_1: # %entry +; MIPS64EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64EB-NEXT: lld $2, 0($1) +; MIPS64EB-NEXT: xor $3, $2, $4 +; MIPS64EB-NEXT: scd $3, 0($1) +; MIPS64EB-NEXT: beqz $3, .LBB4_1 +; MIPS64EB-NEXT: nop +; MIPS64EB-NEXT: # %bb.2: # %entry +; MIPS64EB-NEXT: jr $ra +; MIPS64EB-NEXT: nop +entry: + %0 = atomicrmw xor i64* @x, i64 %incr monotonic + ret i64 %0 + +} + +define i64 @AtomicLoadNand(i64 signext %incr) nounwind { +; MIPS4-LABEL: AtomicLoadNand: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand))) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB5_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: lld $2, 0($1) +; MIPS4-NEXT: and $3, $2, $4 +; MIPS4-NEXT: nor $3, $zero, $3 +; MIPS4-NEXT: scd $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB5_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: nop +; +; MIPS64-LABEL: AtomicLoadNand: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand))) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB5_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: lld $2, 0($1) +; MIPS64-NEXT: and $3, $2, $4 +; MIPS64-NEXT: nor $3, $zero, $3 +; MIPS64-NEXT: scd $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB5_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: nop +; +; MIPS64R2-LABEL: AtomicLoadNand: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand))) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB5_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: lld $2, 0($1) +; MIPS64R2-NEXT: and $3, $2, $4 +; MIPS64R2-NEXT: nor $3, $zero, $3 +; MIPS64R2-NEXT: scd $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB5_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: nop +; +; MIPS64R6-LABEL: AtomicLoadNand: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand))) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB5_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: lld $2, 0($1) +; MIPS64R6-NEXT: and $3, $2, $4 +; MIPS64R6-NEXT: nor $3, $zero, $3 +; MIPS64R6-NEXT: scd $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB5_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: jrc $ra +; +; MIPS64R6O0-LABEL: AtomicLoadNand: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand))) +; MIPS64R6O0-NEXT: move $25, $4 +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: .LBB5_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: lld $2, 0($1) +; MIPS64R6O0-NEXT: and $3, $2, $4 +; MIPS64R6O0-NEXT: nor $3, $zero, $3 +; MIPS64R6O0-NEXT: scd $3, 0($1) +; MIPS64R6O0-NEXT: beqzc $3, .LBB5_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: sd $25, 8($sp) # 8-byte Folded Spill +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; O1-LABEL: AtomicLoadNand: +; O1: # %bb.0: # %entry +; O1-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand))) +; O1-NEXT: daddu $1, $1, $25 +; O1-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand))) +; O1-NEXT: ld $1, %got_disp(x)($1) +; O1-NEXT: .LBB5_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: lld $2, 0($1) +; O1-NEXT: and $3, $2, $4 +; O1-NEXT: nor $3, $zero, $3 +; O1-NEXT: scd $3, 0($1) +; O1-NEXT: beqz $3, .LBB5_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: nop +; +; O2-LABEL: AtomicLoadNand: +; O2: # %bb.0: # %entry +; O2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand))) +; O2-NEXT: daddu $1, $1, $25 +; O2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand))) +; O2-NEXT: ld $1, %got_disp(x)($1) +; O2-NEXT: .LBB5_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: lld $2, 0($1) +; O2-NEXT: and $3, $2, $4 +; O2-NEXT: nor $3, $zero, $3 +; O2-NEXT: scd $3, 0($1) +; O2-NEXT: beqz $3, .LBB5_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: nop +; +; O3-LABEL: AtomicLoadNand: +; O3: # %bb.0: # %entry +; O3-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand))) +; O3-NEXT: daddu $1, $1, $25 +; O3-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand))) +; O3-NEXT: ld $1, %got_disp(x)($1) +; O3-NEXT: .LBB5_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: lld $2, 0($1) +; O3-NEXT: and $3, $2, $4 +; O3-NEXT: nor $3, $zero, $3 +; O3-NEXT: scd $3, 0($1) +; O3-NEXT: beqz $3, .LBB5_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: nop +; +; MIPS64EB-LABEL: AtomicLoadNand: +; MIPS64EB: # %bb.0: # %entry +; MIPS64EB-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicLoadNand))) +; MIPS64EB-NEXT: daddu $1, $1, $25 +; MIPS64EB-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicLoadNand))) +; MIPS64EB-NEXT: ld $1, %got_disp(x)($1) +; MIPS64EB-NEXT: .LBB5_1: # %entry +; MIPS64EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64EB-NEXT: lld $2, 0($1) +; MIPS64EB-NEXT: and $3, $2, $4 +; MIPS64EB-NEXT: nor $3, $zero, $3 +; MIPS64EB-NEXT: scd $3, 0($1) +; MIPS64EB-NEXT: beqz $3, .LBB5_1 +; MIPS64EB-NEXT: nop +; MIPS64EB-NEXT: # %bb.2: # %entry +; MIPS64EB-NEXT: jr $ra +; MIPS64EB-NEXT: nop +entry: + %0 = atomicrmw nand i64* @x, i64 %incr monotonic + ret i64 %0 + +} + +define i64 @AtomicSwap64(i64 signext %newval) nounwind { +; MIPS4-LABEL: AtomicSwap64: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: daddiu $sp, $sp, -16 +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap64))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap64))) +; MIPS4-NEXT: sd $4, 8($sp) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB6_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: lld $2, 0($1) +; MIPS4-NEXT: move $3, $4 +; MIPS4-NEXT: scd $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB6_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64-LABEL: AtomicSwap64: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: daddiu $sp, $sp, -16 +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap64))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap64))) +; MIPS64-NEXT: sd $4, 8($sp) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB6_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: lld $2, 0($1) +; MIPS64-NEXT: move $3, $4 +; MIPS64-NEXT: scd $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB6_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64R2-LABEL: AtomicSwap64: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: daddiu $sp, $sp, -16 +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap64))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap64))) +; MIPS64R2-NEXT: sd $4, 8($sp) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB6_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: lld $2, 0($1) +; MIPS64R2-NEXT: move $3, $4 +; MIPS64R2-NEXT: scd $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB6_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64R6-LABEL: AtomicSwap64: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap64))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap64))) +; MIPS64R6-NEXT: sd $4, 8($sp) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB6_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: lld $2, 0($1) +; MIPS64R6-NEXT: move $3, $4 +; MIPS64R6-NEXT: scd $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB6_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: jr $ra +; MIPS64R6-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64R6O0-LABEL: AtomicSwap64: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap64))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap64))) +; MIPS64R6O0-NEXT: move $25, $4 +; MIPS64R6O0-NEXT: sd $4, 8($sp) +; MIPS64R6O0-NEXT: ld $4, 8($sp) +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: .LBB6_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: lld $2, 0($1) +; MIPS64R6O0-NEXT: move $3, $4 +; MIPS64R6O0-NEXT: scd $3, 0($1) +; MIPS64R6O0-NEXT: beqzc $3, .LBB6_1 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: sd $25, 0($sp) # 8-byte Folded Spill +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 16 +; MIPS64R6O0-NEXT: jrc $ra +; +; O1-LABEL: AtomicSwap64: +; O1: # %bb.0: # %entry +; O1-NEXT: daddiu $sp, $sp, -16 +; O1-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap64))) +; O1-NEXT: daddu $1, $1, $25 +; O1-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap64))) +; O1-NEXT: sd $4, 8($sp) +; O1-NEXT: ld $1, %got_disp(x)($1) +; O1-NEXT: .LBB6_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: lld $2, 0($1) +; O1-NEXT: move $3, $4 +; O1-NEXT: scd $3, 0($1) +; O1-NEXT: beqz $3, .LBB6_1 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: daddiu $sp, $sp, 16 +; +; O2-LABEL: AtomicSwap64: +; O2: # %bb.0: # %entry +; O2-NEXT: daddiu $sp, $sp, -16 +; O2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap64))) +; O2-NEXT: daddu $1, $1, $25 +; O2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap64))) +; O2-NEXT: sd $4, 8($sp) +; O2-NEXT: ld $1, %got_disp(x)($1) +; O2-NEXT: .LBB6_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: lld $2, 0($1) +; O2-NEXT: move $3, $4 +; O2-NEXT: scd $3, 0($1) +; O2-NEXT: beqz $3, .LBB6_1 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: daddiu $sp, $sp, 16 +; +; O3-LABEL: AtomicSwap64: +; O3: # %bb.0: # %entry +; O3-NEXT: daddiu $sp, $sp, -16 +; O3-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap64))) +; O3-NEXT: sd $4, 8($sp) +; O3-NEXT: daddu $1, $1, $25 +; O3-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap64))) +; O3-NEXT: ld $1, %got_disp(x)($1) +; O3-NEXT: .LBB6_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: lld $2, 0($1) +; O3-NEXT: move $3, $4 +; O3-NEXT: scd $3, 0($1) +; O3-NEXT: beqz $3, .LBB6_1 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64EB-LABEL: AtomicSwap64: +; MIPS64EB: # %bb.0: # %entry +; MIPS64EB-NEXT: daddiu $sp, $sp, -16 +; MIPS64EB-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicSwap64))) +; MIPS64EB-NEXT: daddu $1, $1, $25 +; MIPS64EB-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicSwap64))) +; MIPS64EB-NEXT: sd $4, 8($sp) +; MIPS64EB-NEXT: ld $1, %got_disp(x)($1) +; MIPS64EB-NEXT: .LBB6_1: # %entry +; MIPS64EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64EB-NEXT: lld $2, 0($1) +; MIPS64EB-NEXT: move $3, $4 +; MIPS64EB-NEXT: scd $3, 0($1) +; MIPS64EB-NEXT: beqz $3, .LBB6_1 +; MIPS64EB-NEXT: nop +; MIPS64EB-NEXT: # %bb.2: # %entry +; MIPS64EB-NEXT: jr $ra +; MIPS64EB-NEXT: daddiu $sp, $sp, 16 +entry: + %newval.addr = alloca i64, align 4 + store i64 %newval, i64* %newval.addr, align 4 + %tmp = load i64, i64* %newval.addr, align 4 + %0 = atomicrmw xchg i64* @x, i64 %tmp monotonic + ret i64 %0 + +} + +define i64 @AtomicCmpSwap64(i64 signext %oldval, i64 signext %newval) nounwind { +; MIPS4-LABEL: AtomicCmpSwap64: +; MIPS4: # %bb.0: # %entry +; MIPS4-NEXT: daddiu $sp, $sp, -16 +; MIPS4-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap64))) +; MIPS4-NEXT: daddu $1, $1, $25 +; MIPS4-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap64))) +; MIPS4-NEXT: sd $5, 8($sp) +; MIPS4-NEXT: ld $1, %got_disp(x)($1) +; MIPS4-NEXT: .LBB7_1: # %entry +; MIPS4-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS4-NEXT: lld $2, 0($1) +; MIPS4-NEXT: bne $2, $4, .LBB7_3 +; MIPS4-NEXT: nop +; MIPS4-NEXT: # %bb.2: # %entry +; MIPS4-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS4-NEXT: move $3, $5 +; MIPS4-NEXT: scd $3, 0($1) +; MIPS4-NEXT: beqz $3, .LBB7_1 +; MIPS4-NEXT: nop +; MIPS4-NEXT: .LBB7_3: # %entry +; MIPS4-NEXT: jr $ra +; MIPS4-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64-LABEL: AtomicCmpSwap64: +; MIPS64: # %bb.0: # %entry +; MIPS64-NEXT: daddiu $sp, $sp, -16 +; MIPS64-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap64))) +; MIPS64-NEXT: daddu $1, $1, $25 +; MIPS64-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap64))) +; MIPS64-NEXT: sd $5, 8($sp) +; MIPS64-NEXT: ld $1, %got_disp(x)($1) +; MIPS64-NEXT: .LBB7_1: # %entry +; MIPS64-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64-NEXT: lld $2, 0($1) +; MIPS64-NEXT: bne $2, $4, .LBB7_3 +; MIPS64-NEXT: nop +; MIPS64-NEXT: # %bb.2: # %entry +; MIPS64-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS64-NEXT: move $3, $5 +; MIPS64-NEXT: scd $3, 0($1) +; MIPS64-NEXT: beqz $3, .LBB7_1 +; MIPS64-NEXT: nop +; MIPS64-NEXT: .LBB7_3: # %entry +; MIPS64-NEXT: jr $ra +; MIPS64-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64R2-LABEL: AtomicCmpSwap64: +; MIPS64R2: # %bb.0: # %entry +; MIPS64R2-NEXT: daddiu $sp, $sp, -16 +; MIPS64R2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap64))) +; MIPS64R2-NEXT: daddu $1, $1, $25 +; MIPS64R2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap64))) +; MIPS64R2-NEXT: sd $5, 8($sp) +; MIPS64R2-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R2-NEXT: .LBB7_1: # %entry +; MIPS64R2-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R2-NEXT: lld $2, 0($1) +; MIPS64R2-NEXT: bne $2, $4, .LBB7_3 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: # %bb.2: # %entry +; MIPS64R2-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS64R2-NEXT: move $3, $5 +; MIPS64R2-NEXT: scd $3, 0($1) +; MIPS64R2-NEXT: beqz $3, .LBB7_1 +; MIPS64R2-NEXT: nop +; MIPS64R2-NEXT: .LBB7_3: # %entry +; MIPS64R2-NEXT: jr $ra +; MIPS64R2-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64R6-LABEL: AtomicCmpSwap64: +; MIPS64R6: # %bb.0: # %entry +; MIPS64R6-NEXT: daddiu $sp, $sp, -16 +; MIPS64R6-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap64))) +; MIPS64R6-NEXT: daddu $1, $1, $25 +; MIPS64R6-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap64))) +; MIPS64R6-NEXT: sd $5, 8($sp) +; MIPS64R6-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6-NEXT: .LBB7_1: # %entry +; MIPS64R6-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6-NEXT: lld $2, 0($1) +; MIPS64R6-NEXT: bnec $2, $4, .LBB7_3 +; MIPS64R6-NEXT: # %bb.2: # %entry +; MIPS64R6-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS64R6-NEXT: move $3, $5 +; MIPS64R6-NEXT: scd $3, 0($1) +; MIPS64R6-NEXT: beqzc $3, .LBB7_1 +; MIPS64R6-NEXT: nop +; MIPS64R6-NEXT: .LBB7_3: # %entry +; MIPS64R6-NEXT: jr $ra +; MIPS64R6-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64R6O0-LABEL: AtomicCmpSwap64: +; MIPS64R6O0: # %bb.0: # %entry +; MIPS64R6O0-NEXT: daddiu $sp, $sp, -48 +; MIPS64R6O0-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap64))) +; MIPS64R6O0-NEXT: daddu $1, $1, $25 +; MIPS64R6O0-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap64))) +; MIPS64R6O0-NEXT: move $25, $5 +; MIPS64R6O0-NEXT: move $2, $4 +; MIPS64R6O0-NEXT: sd $5, 40($sp) +; MIPS64R6O0-NEXT: ld $5, 40($sp) +; MIPS64R6O0-NEXT: ld $1, %got_disp(x)($1) +; MIPS64R6O0-NEXT: ld $3, 32($sp) # 8-byte Folded Reload +; MIPS64R6O0-NEXT: .LBB7_1: # %entry +; MIPS64R6O0-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64R6O0-NEXT: lld $6, 0($1) +; MIPS64R6O0-NEXT: bnec $6, $4, .LBB7_3 +; MIPS64R6O0-NEXT: # %bb.2: # %entry +; MIPS64R6O0-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS64R6O0-NEXT: move $7, $5 +; MIPS64R6O0-NEXT: scd $7, 0($1) +; MIPS64R6O0-NEXT: beqzc $7, .LBB7_1 +; MIPS64R6O0-NEXT: .LBB7_3: # %entry +; MIPS64R6O0-NEXT: sd $2, 24($sp) # 8-byte Folded Spill +; MIPS64R6O0-NEXT: move $2, $6 +; MIPS64R6O0-NEXT: sd $6, 32($sp) # 8-byte Folded Spill +; MIPS64R6O0-NEXT: sd $25, 16($sp) # 8-byte Folded Spill +; MIPS64R6O0-NEXT: sd $3, 8($sp) # 8-byte Folded Spill +; MIPS64R6O0-NEXT: daddiu $sp, $sp, 48 +; MIPS64R6O0-NEXT: jrc $ra +; +; O1-LABEL: AtomicCmpSwap64: +; O1: # %bb.0: # %entry +; O1-NEXT: daddiu $sp, $sp, -16 +; O1-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap64))) +; O1-NEXT: daddu $1, $1, $25 +; O1-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap64))) +; O1-NEXT: sd $5, 8($sp) +; O1-NEXT: ld $1, %got_disp(x)($1) +; O1-NEXT: .LBB7_1: # %entry +; O1-NEXT: # =>This Inner Loop Header: Depth=1 +; O1-NEXT: lld $2, 0($1) +; O1-NEXT: bne $2, $4, .LBB7_3 +; O1-NEXT: nop +; O1-NEXT: # %bb.2: # %entry +; O1-NEXT: # in Loop: Header=BB7_1 Depth=1 +; O1-NEXT: move $3, $5 +; O1-NEXT: scd $3, 0($1) +; O1-NEXT: beqz $3, .LBB7_1 +; O1-NEXT: nop +; O1-NEXT: .LBB7_3: # %entry +; O1-NEXT: jr $ra +; O1-NEXT: daddiu $sp, $sp, 16 +; +; O2-LABEL: AtomicCmpSwap64: +; O2: # %bb.0: # %entry +; O2-NEXT: daddiu $sp, $sp, -16 +; O2-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap64))) +; O2-NEXT: daddu $1, $1, $25 +; O2-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap64))) +; O2-NEXT: sd $5, 8($sp) +; O2-NEXT: ld $1, %got_disp(x)($1) +; O2-NEXT: .LBB7_1: # %entry +; O2-NEXT: # =>This Inner Loop Header: Depth=1 +; O2-NEXT: lld $2, 0($1) +; O2-NEXT: bne $2, $4, .LBB7_3 +; O2-NEXT: nop +; O2-NEXT: # %bb.2: # %entry +; O2-NEXT: # in Loop: Header=BB7_1 Depth=1 +; O2-NEXT: move $3, $5 +; O2-NEXT: scd $3, 0($1) +; O2-NEXT: beqz $3, .LBB7_1 +; O2-NEXT: nop +; O2-NEXT: .LBB7_3: # %entry +; O2-NEXT: jr $ra +; O2-NEXT: daddiu $sp, $sp, 16 +; +; O3-LABEL: AtomicCmpSwap64: +; O3: # %bb.0: # %entry +; O3-NEXT: daddiu $sp, $sp, -16 +; O3-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap64))) +; O3-NEXT: sd $5, 8($sp) +; O3-NEXT: daddu $1, $1, $25 +; O3-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap64))) +; O3-NEXT: ld $1, %got_disp(x)($1) +; O3-NEXT: .LBB7_1: # %entry +; O3-NEXT: # =>This Inner Loop Header: Depth=1 +; O3-NEXT: lld $2, 0($1) +; O3-NEXT: bne $2, $4, .LBB7_3 +; O3-NEXT: nop +; O3-NEXT: # %bb.2: # %entry +; O3-NEXT: # in Loop: Header=BB7_1 Depth=1 +; O3-NEXT: move $3, $5 +; O3-NEXT: scd $3, 0($1) +; O3-NEXT: beqz $3, .LBB7_1 +; O3-NEXT: nop +; O3-NEXT: .LBB7_3: # %entry +; O3-NEXT: jr $ra +; O3-NEXT: daddiu $sp, $sp, 16 +; +; MIPS64EB-LABEL: AtomicCmpSwap64: +; MIPS64EB: # %bb.0: # %entry +; MIPS64EB-NEXT: daddiu $sp, $sp, -16 +; MIPS64EB-NEXT: lui $1, %hi(%neg(%gp_rel(AtomicCmpSwap64))) +; MIPS64EB-NEXT: daddu $1, $1, $25 +; MIPS64EB-NEXT: daddiu $1, $1, %lo(%neg(%gp_rel(AtomicCmpSwap64))) +; MIPS64EB-NEXT: sd $5, 8($sp) +; MIPS64EB-NEXT: ld $1, %got_disp(x)($1) +; MIPS64EB-NEXT: .LBB7_1: # %entry +; MIPS64EB-NEXT: # =>This Inner Loop Header: Depth=1 +; MIPS64EB-NEXT: lld $2, 0($1) +; MIPS64EB-NEXT: bne $2, $4, .LBB7_3 +; MIPS64EB-NEXT: nop +; MIPS64EB-NEXT: # %bb.2: # %entry +; MIPS64EB-NEXT: # in Loop: Header=BB7_1 Depth=1 +; MIPS64EB-NEXT: move $3, $5 +; MIPS64EB-NEXT: scd $3, 0($1) +; MIPS64EB-NEXT: beqz $3, .LBB7_1 +; MIPS64EB-NEXT: nop +; MIPS64EB-NEXT: .LBB7_3: # %entry +; MIPS64EB-NEXT: jr $ra +; MIPS64EB-NEXT: daddiu $sp, $sp, 16 +entry: + %newval.addr = alloca i64, align 4 + store i64 %newval, i64* %newval.addr, align 4 + %tmp = load i64, i64* %newval.addr, align 4 + %0 = cmpxchg i64* @x, i64 %oldval, i64 %tmp monotonic monotonic + %1 = extractvalue { i64, i1 } %0, 0 + ret i64 %1 + +} Index: llvm/trunk/lib/Target/Mips/MipsISelLowering.h =================================================================== --- llvm/trunk/lib/Target/Mips/MipsISelLowering.h (revision 336327) +++ llvm/trunk/lib/Target/Mips/MipsISelLowering.h (revision 336328) @@ -1,718 +1,714 @@ //===- MipsISelLowering.h - Mips DAG Lowering Interface ---------*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines the interfaces that Mips uses to lower LLVM code into a // selection DAG. // //===----------------------------------------------------------------------===// #ifndef LLVM_LIB_TARGET_MIPS_MIPSISELLOWERING_H #define LLVM_LIB_TARGET_MIPS_MIPSISELLOWERING_H #include "MCTargetDesc/MipsABIInfo.h" #include "MCTargetDesc/MipsBaseInfo.h" #include "MCTargetDesc/MipsMCTargetDesc.h" #include "Mips.h" #include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/ISDOpcodes.h" #include "llvm/CodeGen/MachineMemOperand.h" #include "llvm/CodeGen/SelectionDAG.h" #include "llvm/CodeGen/SelectionDAGNodes.h" #include "llvm/CodeGen/TargetLowering.h" #include "llvm/CodeGen/ValueTypes.h" #include "llvm/IR/CallingConv.h" #include "llvm/IR/InlineAsm.h" #include "llvm/IR/Type.h" #include "llvm/Support/MachineValueType.h" #include "llvm/Target/TargetMachine.h" #include #include #include #include #include #include namespace llvm { class Argument; class CCState; class CCValAssign; class FastISel; class FunctionLoweringInfo; class MachineBasicBlock; class MachineFrameInfo; class MachineInstr; class MipsCCState; class MipsFunctionInfo; class MipsSubtarget; class MipsTargetMachine; class TargetLibraryInfo; class TargetRegisterClass; namespace MipsISD { enum NodeType : unsigned { // Start the numbering from where ISD NodeType finishes. FIRST_NUMBER = ISD::BUILTIN_OP_END, // Jump and link (call) JmpLink, // Tail call TailCall, // Get the Highest (63-48) 16 bits from a 64-bit immediate Highest, // Get the Higher (47-32) 16 bits from a 64-bit immediate Higher, // Get the High 16 bits from a 32/64-bit immediate // No relation with Mips Hi register Hi, // Get the Lower 16 bits from a 32/64-bit immediate // No relation with Mips Lo register Lo, // Get the High 16 bits from a 32 bit immediate for accessing the GOT. GotHi, // Handle gp_rel (small data/bss sections) relocation. GPRel, // Thread Pointer ThreadPointer, // Vector Floating Point Multiply and Subtract FMS, // Floating Point Branch Conditional FPBrcond, // Floating Point Compare FPCmp, // Floating point select FSELECT, // Node used to generate an MTC1 i32 to f64 instruction MTC1_D64, // Floating Point Conditional Moves CMovFP_T, CMovFP_F, // FP-to-int truncation node. TruncIntFP, // Return Ret, // Interrupt, exception, error trap Return ERet, // Software Exception Return. EH_RETURN, // Node used to extract integer from accumulator. MFHI, MFLO, // Node used to insert integers to accumulator. MTLOHI, // Mult nodes. Mult, Multu, // MAdd/Sub nodes MAdd, MAddu, MSub, MSubu, // DivRem(u) DivRem, DivRemU, DivRem16, DivRemU16, BuildPairF64, ExtractElementF64, Wrapper, DynAlloc, Sync, Ext, Ins, CIns, // EXTR.W instrinsic nodes. EXTP, EXTPDP, EXTR_S_H, EXTR_W, EXTR_R_W, EXTR_RS_W, SHILO, MTHLIP, // DPA.W intrinsic nodes. MULSAQ_S_W_PH, MAQ_S_W_PHL, MAQ_S_W_PHR, MAQ_SA_W_PHL, MAQ_SA_W_PHR, DPAU_H_QBL, DPAU_H_QBR, DPSU_H_QBL, DPSU_H_QBR, DPAQ_S_W_PH, DPSQ_S_W_PH, DPAQ_SA_L_W, DPSQ_SA_L_W, DPA_W_PH, DPS_W_PH, DPAQX_S_W_PH, DPAQX_SA_W_PH, DPAX_W_PH, DPSX_W_PH, DPSQX_S_W_PH, DPSQX_SA_W_PH, MULSA_W_PH, MULT, MULTU, MADD_DSP, MADDU_DSP, MSUB_DSP, MSUBU_DSP, // DSP shift nodes. SHLL_DSP, SHRA_DSP, SHRL_DSP, // DSP setcc and select_cc nodes. SETCC_DSP, SELECT_CC_DSP, // Vector comparisons. // These take a vector and return a boolean. VALL_ZERO, VANY_ZERO, VALL_NONZERO, VANY_NONZERO, // These take a vector and return a vector bitmask. VCEQ, VCLE_S, VCLE_U, VCLT_S, VCLT_U, // Vector Shuffle with mask as an operand VSHF, // Generic shuffle SHF, // 4-element set shuffle. ILVEV, // Interleave even elements ILVOD, // Interleave odd elements ILVL, // Interleave left elements ILVR, // Interleave right elements PCKEV, // Pack even elements PCKOD, // Pack odd elements // Vector Lane Copy INSVE, // Copy element from one vector to another // Combined (XOR (OR $a, $b), -1) VNOR, // Extended vector element extraction VEXTRACT_SEXT_ELT, VEXTRACT_ZEXT_ELT, // Load/Store Left/Right nodes. LWL = ISD::FIRST_TARGET_MEMORY_OPCODE, LWR, SWL, SWR, LDL, LDR, SDL, SDR }; } // ene namespace MipsISD //===--------------------------------------------------------------------===// // TargetLowering Implementation //===--------------------------------------------------------------------===// class MipsTargetLowering : public TargetLowering { bool isMicroMips; public: explicit MipsTargetLowering(const MipsTargetMachine &TM, const MipsSubtarget &STI); static const MipsTargetLowering *create(const MipsTargetMachine &TM, const MipsSubtarget &STI); /// createFastISel - This method returns a target specific FastISel object, /// or null if the target does not support "fast" ISel. FastISel *createFastISel(FunctionLoweringInfo &funcInfo, const TargetLibraryInfo *libInfo) const override; MVT getScalarShiftAmountTy(const DataLayout &, EVT) const override { return MVT::i32; } bool isCheapToSpeculateCttz() const override; bool isCheapToSpeculateCtlz() const override; /// Return the register type for a given MVT, ensuring vectors are treated /// as a series of gpr sized integers. MVT getRegisterTypeForCallingConv(LLVMContext &Context, EVT VT) const override; /// Return the number of registers for a given MVT, ensuring vectors are /// treated as a series of gpr sized integers. unsigned getNumRegistersForCallingConv(LLVMContext &Context, EVT VT) const override; /// Break down vectors to the correct number of gpr sized integers. unsigned getVectorTypeBreakdownForCallingConv( LLVMContext &Context, EVT VT, EVT &IntermediateVT, unsigned &NumIntermediates, MVT &RegisterVT) const override; /// Return the correct alignment for the current calling convention. unsigned getABIAlignmentForCallingConv(Type *ArgTy, DataLayout DL) const override { if (ArgTy->isVectorTy()) return std::min(DL.getABITypeAlignment(ArgTy), 8U); return DL.getABITypeAlignment(ArgTy); } ISD::NodeType getExtendForAtomicOps() const override { return ISD::SIGN_EXTEND; } void LowerOperationWrapper(SDNode *N, SmallVectorImpl &Results, SelectionDAG &DAG) const override; /// LowerOperation - Provide custom lowering hooks for some operations. SDValue LowerOperation(SDValue Op, SelectionDAG &DAG) const override; /// ReplaceNodeResults - Replace the results of node with an illegal result /// type with new values built out of custom code. /// void ReplaceNodeResults(SDNode *N, SmallVectorImpl&Results, SelectionDAG &DAG) const override; /// getTargetNodeName - This method returns the name of a target specific // DAG node. const char *getTargetNodeName(unsigned Opcode) const override; /// getSetCCResultType - get the ISD::SETCC result ValueType EVT getSetCCResultType(const DataLayout &DL, LLVMContext &Context, EVT VT) const override; SDValue PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const override; MachineBasicBlock * EmitInstrWithCustomInserter(MachineInstr &MI, MachineBasicBlock *MBB) const override; void HandleByVal(CCState *, unsigned &, unsigned) const override; unsigned getRegisterByName(const char* RegName, EVT VT, SelectionDAG &DAG) const override; /// If a physical register, this returns the register that receives the /// exception address on entry to an EH pad. unsigned getExceptionPointerRegister(const Constant *PersonalityFn) const override { return ABI.IsN64() ? Mips::A0_64 : Mips::A0; } /// If a physical register, this returns the register that receives the /// exception typeid on entry to a landing pad. unsigned getExceptionSelectorRegister(const Constant *PersonalityFn) const override { return ABI.IsN64() ? Mips::A1_64 : Mips::A1; } /// Returns true if a cast between SrcAS and DestAS is a noop. bool isNoopAddrSpaceCast(unsigned SrcAS, unsigned DestAS) const override { // Mips doesn't have any special address spaces so we just reserve // the first 256 for software use (e.g. OpenCL) and treat casts // between them as noops. return SrcAS < 256 && DestAS < 256; } bool isJumpTableRelative() const override { return getTargetMachine().isPositionIndependent(); } CCAssignFn *CCAssignFnForCall() const; CCAssignFn *CCAssignFnForReturn() const; protected: SDValue getGlobalReg(SelectionDAG &DAG, EVT Ty) const; // This method creates the following nodes, which are necessary for // computing a local symbol's address: // // (add (load (wrapper $gp, %got(sym)), %lo(sym)) template SDValue getAddrLocal(NodeTy *N, const SDLoc &DL, EVT Ty, SelectionDAG &DAG, bool IsN32OrN64) const { unsigned GOTFlag = IsN32OrN64 ? MipsII::MO_GOT_PAGE : MipsII::MO_GOT; SDValue GOT = DAG.getNode(MipsISD::Wrapper, DL, Ty, getGlobalReg(DAG, Ty), getTargetNode(N, Ty, DAG, GOTFlag)); SDValue Load = DAG.getLoad(Ty, DL, DAG.getEntryNode(), GOT, MachinePointerInfo::getGOT(DAG.getMachineFunction())); unsigned LoFlag = IsN32OrN64 ? MipsII::MO_GOT_OFST : MipsII::MO_ABS_LO; SDValue Lo = DAG.getNode(MipsISD::Lo, DL, Ty, getTargetNode(N, Ty, DAG, LoFlag)); return DAG.getNode(ISD::ADD, DL, Ty, Load, Lo); } // This method creates the following nodes, which are necessary for // computing a global symbol's address: // // (load (wrapper $gp, %got(sym))) template SDValue getAddrGlobal(NodeTy *N, const SDLoc &DL, EVT Ty, SelectionDAG &DAG, unsigned Flag, SDValue Chain, const MachinePointerInfo &PtrInfo) const { SDValue Tgt = DAG.getNode(MipsISD::Wrapper, DL, Ty, getGlobalReg(DAG, Ty), getTargetNode(N, Ty, DAG, Flag)); return DAG.getLoad(Ty, DL, Chain, Tgt, PtrInfo); } // This method creates the following nodes, which are necessary for // computing a global symbol's address in large-GOT mode: // // (load (wrapper (add %hi(sym), $gp), %lo(sym))) template SDValue getAddrGlobalLargeGOT(NodeTy *N, const SDLoc &DL, EVT Ty, SelectionDAG &DAG, unsigned HiFlag, unsigned LoFlag, SDValue Chain, const MachinePointerInfo &PtrInfo) const { SDValue Hi = DAG.getNode(MipsISD::GotHi, DL, Ty, getTargetNode(N, Ty, DAG, HiFlag)); Hi = DAG.getNode(ISD::ADD, DL, Ty, Hi, getGlobalReg(DAG, Ty)); SDValue Wrapper = DAG.getNode(MipsISD::Wrapper, DL, Ty, Hi, getTargetNode(N, Ty, DAG, LoFlag)); return DAG.getLoad(Ty, DL, Chain, Wrapper, PtrInfo); } // This method creates the following nodes, which are necessary for // computing a symbol's address in non-PIC mode: // // (add %hi(sym), %lo(sym)) // // This method covers O32, N32 and N64 in sym32 mode. template SDValue getAddrNonPIC(NodeTy *N, const SDLoc &DL, EVT Ty, SelectionDAG &DAG) const { SDValue Hi = getTargetNode(N, Ty, DAG, MipsII::MO_ABS_HI); SDValue Lo = getTargetNode(N, Ty, DAG, MipsII::MO_ABS_LO); return DAG.getNode(ISD::ADD, DL, Ty, DAG.getNode(MipsISD::Hi, DL, Ty, Hi), DAG.getNode(MipsISD::Lo, DL, Ty, Lo)); } // This method creates the following nodes, which are necessary for // computing a symbol's address in non-PIC mode for N64. // // (add (shl (add (shl (add %highest(sym), %higher(sim)), 16), %high(sym)), // 16), %lo(%sym)) // // FIXME: This method is not efficent for (micro)MIPS64R6. template SDValue getAddrNonPICSym64(NodeTy *N, const SDLoc &DL, EVT Ty, SelectionDAG &DAG) const { SDValue Hi = getTargetNode(N, Ty, DAG, MipsII::MO_ABS_HI); SDValue Lo = getTargetNode(N, Ty, DAG, MipsII::MO_ABS_LO); SDValue Highest = DAG.getNode(MipsISD::Highest, DL, Ty, getTargetNode(N, Ty, DAG, MipsII::MO_HIGHEST)); SDValue Higher = getTargetNode(N, Ty, DAG, MipsII::MO_HIGHER); SDValue HigherPart = DAG.getNode(ISD::ADD, DL, Ty, Highest, DAG.getNode(MipsISD::Higher, DL, Ty, Higher)); SDValue Cst = DAG.getConstant(16, DL, MVT::i32); SDValue Shift = DAG.getNode(ISD::SHL, DL, Ty, HigherPart, Cst); SDValue Add = DAG.getNode(ISD::ADD, DL, Ty, Shift, DAG.getNode(MipsISD::Hi, DL, Ty, Hi)); SDValue Shift2 = DAG.getNode(ISD::SHL, DL, Ty, Add, Cst); return DAG.getNode(ISD::ADD, DL, Ty, Shift2, DAG.getNode(MipsISD::Lo, DL, Ty, Lo)); } // This method creates the following nodes, which are necessary for // computing a symbol's address using gp-relative addressing: // // (add $gp, %gp_rel(sym)) template SDValue getAddrGPRel(NodeTy *N, const SDLoc &DL, EVT Ty, SelectionDAG &DAG, bool IsN64) const { SDValue GPRel = getTargetNode(N, Ty, DAG, MipsII::MO_GPREL); return DAG.getNode( ISD::ADD, DL, Ty, DAG.getRegister(IsN64 ? Mips::GP_64 : Mips::GP, Ty), DAG.getNode(MipsISD::GPRel, DL, DAG.getVTList(Ty), GPRel)); } /// This function fills Ops, which is the list of operands that will later /// be used when a function call node is created. It also generates /// copyToReg nodes to set up argument registers. virtual void getOpndList(SmallVectorImpl &Ops, std::deque> &RegsToPass, bool IsPICCall, bool GlobalOrExternal, bool InternalLinkage, bool IsCallReloc, CallLoweringInfo &CLI, SDValue Callee, SDValue Chain) const; protected: SDValue lowerLOAD(SDValue Op, SelectionDAG &DAG) const; SDValue lowerSTORE(SDValue Op, SelectionDAG &DAG) const; // Subtarget Info const MipsSubtarget &Subtarget; // Cache the ABI from the TargetMachine, we use it everywhere. const MipsABIInfo &ABI; private: // Create a TargetGlobalAddress node. SDValue getTargetNode(GlobalAddressSDNode *N, EVT Ty, SelectionDAG &DAG, unsigned Flag) const; // Create a TargetExternalSymbol node. SDValue getTargetNode(ExternalSymbolSDNode *N, EVT Ty, SelectionDAG &DAG, unsigned Flag) const; // Create a TargetBlockAddress node. SDValue getTargetNode(BlockAddressSDNode *N, EVT Ty, SelectionDAG &DAG, unsigned Flag) const; // Create a TargetJumpTable node. SDValue getTargetNode(JumpTableSDNode *N, EVT Ty, SelectionDAG &DAG, unsigned Flag) const; // Create a TargetConstantPool node. SDValue getTargetNode(ConstantPoolSDNode *N, EVT Ty, SelectionDAG &DAG, unsigned Flag) const; // Lower Operand helpers SDValue LowerCallResult(SDValue Chain, SDValue InFlag, CallingConv::ID CallConv, bool isVarArg, const SmallVectorImpl &Ins, const SDLoc &dl, SelectionDAG &DAG, SmallVectorImpl &InVals, TargetLowering::CallLoweringInfo &CLI) const; // Lower Operand specifics SDValue lowerBRCOND(SDValue Op, SelectionDAG &DAG) const; SDValue lowerConstantPool(SDValue Op, SelectionDAG &DAG) const; SDValue lowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const; SDValue lowerBlockAddress(SDValue Op, SelectionDAG &DAG) const; SDValue lowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const; SDValue lowerJumpTable(SDValue Op, SelectionDAG &DAG) const; SDValue lowerSELECT(SDValue Op, SelectionDAG &DAG) const; SDValue lowerSETCC(SDValue Op, SelectionDAG &DAG) const; SDValue lowerVASTART(SDValue Op, SelectionDAG &DAG) const; SDValue lowerVAARG(SDValue Op, SelectionDAG &DAG) const; SDValue lowerFCOPYSIGN(SDValue Op, SelectionDAG &DAG) const; SDValue lowerFABS(SDValue Op, SelectionDAG &DAG) const; SDValue lowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const; SDValue lowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const; SDValue lowerEH_RETURN(SDValue Op, SelectionDAG &DAG) const; SDValue lowerATOMIC_FENCE(SDValue Op, SelectionDAG& DAG) const; SDValue lowerShiftLeftParts(SDValue Op, SelectionDAG& DAG) const; SDValue lowerShiftRightParts(SDValue Op, SelectionDAG& DAG, bool IsSRA) const; SDValue lowerEH_DWARF_CFA(SDValue Op, SelectionDAG &DAG) const; SDValue lowerFP_TO_SINT(SDValue Op, SelectionDAG &DAG) const; /// isEligibleForTailCallOptimization - Check whether the call is eligible /// for tail call optimization. virtual bool isEligibleForTailCallOptimization(const CCState &CCInfo, unsigned NextStackOffset, const MipsFunctionInfo &FI) const = 0; /// copyByValArg - Copy argument registers which were used to pass a byval /// argument to the stack. Create a stack frame object for the byval /// argument. void copyByValRegs(SDValue Chain, const SDLoc &DL, std::vector &OutChains, SelectionDAG &DAG, const ISD::ArgFlagsTy &Flags, SmallVectorImpl &InVals, const Argument *FuncArg, unsigned FirstReg, unsigned LastReg, const CCValAssign &VA, MipsCCState &State) const; /// passByValArg - Pass a byval argument in registers or on stack. void passByValArg(SDValue Chain, const SDLoc &DL, std::deque> &RegsToPass, SmallVectorImpl &MemOpChains, SDValue StackPtr, MachineFrameInfo &MFI, SelectionDAG &DAG, SDValue Arg, unsigned FirstReg, unsigned LastReg, const ISD::ArgFlagsTy &Flags, bool isLittle, const CCValAssign &VA) const; /// writeVarArgRegs - Write variable function arguments passed in registers /// to the stack. Also create a stack frame object for the first variable /// argument. void writeVarArgRegs(std::vector &OutChains, SDValue Chain, const SDLoc &DL, SelectionDAG &DAG, CCState &State) const; SDValue LowerFormalArguments(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, const SmallVectorImpl &Ins, const SDLoc &dl, SelectionDAG &DAG, SmallVectorImpl &InVals) const override; SDValue passArgOnStack(SDValue StackPtr, unsigned Offset, SDValue Chain, SDValue Arg, const SDLoc &DL, bool IsTailCall, SelectionDAG &DAG) const; SDValue LowerCall(TargetLowering::CallLoweringInfo &CLI, SmallVectorImpl &InVals) const override; bool CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF, bool isVarArg, const SmallVectorImpl &Outs, LLVMContext &Context) const override; SDValue LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool isVarArg, const SmallVectorImpl &Outs, const SmallVectorImpl &OutVals, const SDLoc &dl, SelectionDAG &DAG) const override; SDValue LowerInterruptReturn(SmallVectorImpl &RetOps, const SDLoc &DL, SelectionDAG &DAG) const; bool shouldSignExtendTypeInLibCall(EVT Type, bool IsSigned) const override; // Inline asm support ConstraintType getConstraintType(StringRef Constraint) const override; /// Examine constraint string and operand type and determine a weight value. /// The operand object must already have been set up with the operand type. ConstraintWeight getSingleConstraintMatchWeight( AsmOperandInfo &info, const char *constraint) const override; /// This function parses registers that appear in inline-asm constraints. /// It returns pair (0, 0) on failure. std::pair parseRegForInlineAsmConstraint(StringRef C, MVT VT) const; std::pair getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const override; /// LowerAsmOperandForConstraint - Lower the specified operand into the Ops /// vector. If it is invalid, don't add anything to Ops. If hasMemory is /// true it means one of the asm constraint of the inline asm instruction /// being processed is 'm'. void LowerAsmOperandForConstraint(SDValue Op, std::string &Constraint, std::vector &Ops, SelectionDAG &DAG) const override; unsigned getInlineAsmMemConstraint(StringRef ConstraintCode) const override { if (ConstraintCode == "R") return InlineAsm::Constraint_R; else if (ConstraintCode == "ZC") return InlineAsm::Constraint_ZC; return TargetLowering::getInlineAsmMemConstraint(ConstraintCode); } bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, Type *Ty, unsigned AS, Instruction *I = nullptr) const override; bool isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const override; EVT getOptimalMemOpType(uint64_t Size, unsigned DstAlign, unsigned SrcAlign, bool IsMemset, bool ZeroMemset, bool MemcpyStrSrc, MachineFunction &MF) const override; /// isFPImmLegal - Returns true if the target can instruction select the /// specified FP immediate natively. If false, the legalizer will /// materialize the FP immediate as a load from a constant pool. bool isFPImmLegal(const APFloat &Imm, EVT VT) const override; unsigned getJumpTableEncoding() const override; bool useSoftFloat() const override; bool shouldInsertFencesForAtomic(const Instruction *I) const override { return true; } /// Emit a sign-extension using sll/sra, seb, or seh appropriately. MachineBasicBlock *emitSignExtendToI32InReg(MachineInstr &MI, MachineBasicBlock *BB, unsigned Size, unsigned DstReg, unsigned SrcRec) const; - MachineBasicBlock *emitAtomicBinary(MachineInstr &MI, MachineBasicBlock *BB, - unsigned Size, unsigned BinOpcode, - bool Nand = false) const; + MachineBasicBlock *emitAtomicBinary(MachineInstr &MI, + MachineBasicBlock *BB) const; MachineBasicBlock *emitAtomicBinaryPartword(MachineInstr &MI, MachineBasicBlock *BB, - unsigned Size, - unsigned BinOpcode, - bool Nand = false) const; + unsigned Size) const; MachineBasicBlock *emitAtomicCmpSwap(MachineInstr &MI, - MachineBasicBlock *BB, - unsigned Size) const; + MachineBasicBlock *BB) const; MachineBasicBlock *emitAtomicCmpSwapPartword(MachineInstr &MI, MachineBasicBlock *BB, unsigned Size) const; MachineBasicBlock *emitSEL_D(MachineInstr &MI, MachineBasicBlock *BB) const; MachineBasicBlock *emitPseudoSELECT(MachineInstr &MI, MachineBasicBlock *BB, bool isFPCmp, unsigned Opc) const; }; /// Create MipsTargetLowering objects. const MipsTargetLowering * createMips16TargetLowering(const MipsTargetMachine &TM, const MipsSubtarget &STI); const MipsTargetLowering * createMipsSETargetLowering(const MipsTargetMachine &TM, const MipsSubtarget &STI); namespace Mips { FastISel *createFastISel(FunctionLoweringInfo &funcInfo, const TargetLibraryInfo *libInfo); } // end namespace Mips } // end namespace llvm #endif // LLVM_LIB_TARGET_MIPS_MIPSISELLOWERING_H Index: llvm/trunk/lib/Target/Mips/MipsExpandPseudo.cpp =================================================================== --- llvm/trunk/lib/Target/Mips/MipsExpandPseudo.cpp (revision 0) +++ llvm/trunk/lib/Target/Mips/MipsExpandPseudo.cpp (revision 336328) @@ -0,0 +1,702 @@ +//===-- MipsExpandPseudoInsts.cpp - Expand pseudo instructions ------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains a pass that expands pseudo instructions into target +// instructions to allow proper scheduling, if-conversion, and other late +// optimizations. This pass should be run after register allocation but before +// the post-regalloc scheduling pass. +// +// This is currently only used for expanding atomic pseudos after register +// allocation. We do this to avoid the fast register allocator introducing +// spills between ll and sc. These stores cause some MIPS implementations to +// abort the atomic RMW sequence. +// +//===----------------------------------------------------------------------===// + +#include "Mips.h" +#include "MipsInstrInfo.h" +#include "MipsSubtarget.h" +#include "llvm/CodeGen/LivePhysRegs.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineInstrBuilder.h" + +using namespace llvm; + +#define DEBUG_TYPE "mips-pseudo" + +namespace { + class MipsExpandPseudo : public MachineFunctionPass { + public: + static char ID; + MipsExpandPseudo() : MachineFunctionPass(ID) {} + + const MipsInstrInfo *TII; + const MipsSubtarget *STI; + + bool runOnMachineFunction(MachineFunction &Fn) override; + + MachineFunctionProperties getRequiredProperties() const override { + return MachineFunctionProperties().set( + MachineFunctionProperties::Property::NoVRegs); + } + + StringRef getPassName() const override { + return "Mips pseudo instruction expansion pass"; + } + + private: + bool expandAtomicCmpSwap(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + MachineBasicBlock::iterator &NextMBBI); + bool expandAtomicCmpSwapSubword(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + MachineBasicBlock::iterator &NextMBBI); + + bool expandAtomicBinOp(MachineBasicBlock &BB, + MachineBasicBlock::iterator I, + MachineBasicBlock::iterator &NMBBI, unsigned Size); + bool expandAtomicBinOpSubword(MachineBasicBlock &BB, + MachineBasicBlock::iterator I, + MachineBasicBlock::iterator &NMBBI); + + bool expandMI(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI, + MachineBasicBlock::iterator &NMBB); + bool expandMBB(MachineBasicBlock &MBB); + }; + char MipsExpandPseudo::ID = 0; +} + +bool MipsExpandPseudo::expandAtomicCmpSwapSubword( + MachineBasicBlock &BB, MachineBasicBlock::iterator I, + MachineBasicBlock::iterator &NMBBI) { + + MachineFunction *MF = BB.getParent(); + + const bool ArePtrs64bit = STI->getABI().ArePtrs64bit(); + DebugLoc DL = I->getDebugLoc(); + unsigned LL, SC; + + unsigned ZERO = Mips::ZERO; + unsigned BNE = Mips::BNE; + unsigned BEQ = Mips::BEQ; + unsigned SEOp = + I->getOpcode() == Mips::ATOMIC_CMP_SWAP_I8_POSTRA ? Mips::SEB : Mips::SEH; + + if (STI->inMicroMipsMode()) { + LL = STI->hasMips32r6() ? Mips::LL_MMR6 : Mips::LL_MM; + SC = STI->hasMips32r6() ? Mips::SC_MMR6 : Mips::SC_MM; + BNE = STI->hasMips32r6() ? Mips::BNEC_MMR6 : Mips::BNE_MM; + BEQ = STI->hasMips32r6() ? Mips::BEQC_MMR6 : Mips::BEQ_MM; + } else { + LL = STI->hasMips32r6() ? (ArePtrs64bit ? Mips::LL64_R6 : Mips::LL_R6) + : (ArePtrs64bit ? Mips::LL64 : Mips::LL); + SC = STI->hasMips32r6() ? (ArePtrs64bit ? Mips::SC64_R6 : Mips::SC_R6) + : (ArePtrs64bit ? Mips::SC64 : Mips::SC); + } + + unsigned Dest = I->getOperand(0).getReg(); + unsigned Ptr = I->getOperand(1).getReg(); + unsigned Mask = I->getOperand(2).getReg(); + unsigned ShiftCmpVal = I->getOperand(3).getReg(); + unsigned Mask2 = I->getOperand(4).getReg(); + unsigned ShiftNewVal = I->getOperand(5).getReg(); + unsigned ShiftAmnt = I->getOperand(6).getReg(); + unsigned Scratch = I->getOperand(7).getReg(); + unsigned Scratch2 = I->getOperand(8).getReg(); + + // insert new blocks after the current block + const BasicBlock *LLVM_BB = BB.getBasicBlock(); + MachineBasicBlock *loop1MBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *loop2MBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *sinkMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineFunction::iterator It = ++BB.getIterator(); + MF->insert(It, loop1MBB); + MF->insert(It, loop2MBB); + MF->insert(It, sinkMBB); + MF->insert(It, exitMBB); + + // Transfer the remainder of BB and its successor edges to exitMBB. + exitMBB->splice(exitMBB->begin(), &BB, + std::next(MachineBasicBlock::iterator(I)), BB.end()); + exitMBB->transferSuccessorsAndUpdatePHIs(&BB); + + // thisMBB: + // ... + // fallthrough --> loop1MBB + BB.addSuccessor(loop1MBB, BranchProbability::getOne()); + loop1MBB->addSuccessor(sinkMBB); + loop1MBB->addSuccessor(loop2MBB); + loop1MBB->normalizeSuccProbs(); + loop2MBB->addSuccessor(loop1MBB); + loop2MBB->addSuccessor(sinkMBB); + loop2MBB->normalizeSuccProbs(); + sinkMBB->addSuccessor(exitMBB, BranchProbability::getOne()); + + // loop1MBB: + // ll dest, 0(ptr) + // and Mask', dest, Mask + // bne Mask', ShiftCmpVal, exitMBB + BuildMI(loop1MBB, DL, TII->get(LL), Scratch).addReg(Ptr).addImm(0); + BuildMI(loop1MBB, DL, TII->get(Mips::AND), Scratch2) + .addReg(Scratch) + .addReg(Mask); + BuildMI(loop1MBB, DL, TII->get(BNE)) + .addReg(Scratch2).addReg(ShiftCmpVal).addMBB(sinkMBB); + + // loop2MBB: + // and dest, dest, mask2 + // or dest, dest, ShiftNewVal + // sc dest, dest, 0(ptr) + // beq dest, $0, loop1MBB + BuildMI(loop2MBB, DL, TII->get(Mips::AND), Scratch) + .addReg(Scratch, RegState::Kill) + .addReg(Mask2); + BuildMI(loop2MBB, DL, TII->get(Mips::OR), Scratch) + .addReg(Scratch, RegState::Kill) + .addReg(ShiftNewVal); + BuildMI(loop2MBB, DL, TII->get(SC), Scratch) + .addReg(Scratch, RegState::Kill) + .addReg(Ptr) + .addImm(0); + BuildMI(loop2MBB, DL, TII->get(BEQ)) + .addReg(Scratch, RegState::Kill) + .addReg(ZERO) + .addMBB(loop1MBB); + + // sinkMBB: + // srl srlres, Mask', shiftamt + // sign_extend dest,srlres + BuildMI(sinkMBB, DL, TII->get(Mips::SRLV), Dest) + .addReg(Scratch2) + .addReg(ShiftAmnt); + if (STI->hasMips32r2()) { + BuildMI(sinkMBB, DL, TII->get(SEOp), Dest).addReg(Dest); + } else { + const unsigned ShiftImm = + I->getOpcode() == Mips::ATOMIC_CMP_SWAP_I16_POSTRA ? 16 : 24; + BuildMI(sinkMBB, DL, TII->get(Mips::SLL), Dest) + .addReg(Dest, RegState::Kill) + .addImm(ShiftImm); + BuildMI(sinkMBB, DL, TII->get(Mips::SRA), Dest) + .addReg(Dest, RegState::Kill) + .addImm(ShiftImm); + } + + LivePhysRegs LiveRegs; + computeAndAddLiveIns(LiveRegs, *loop1MBB); + computeAndAddLiveIns(LiveRegs, *loop2MBB); + computeAndAddLiveIns(LiveRegs, *sinkMBB); + computeAndAddLiveIns(LiveRegs, *exitMBB); + + NMBBI = BB.end(); + I->eraseFromParent(); + return true; +} + +bool MipsExpandPseudo::expandAtomicCmpSwap(MachineBasicBlock &BB, + MachineBasicBlock::iterator I, + MachineBasicBlock::iterator &NMBBI) { + + const unsigned Size = + I->getOpcode() == Mips::ATOMIC_CMP_SWAP_I32_POSTRA ? 4 : 8; + MachineFunction *MF = BB.getParent(); + + const bool ArePtrs64bit = STI->getABI().ArePtrs64bit(); + DebugLoc DL = I->getDebugLoc(); + + unsigned LL, SC, ZERO, BNE, BEQ, MOVE; + + if (Size == 4) { + if (STI->inMicroMipsMode()) { + LL = STI->hasMips32r6() ? Mips::LL_MMR6 : Mips::LL_MM; + SC = STI->hasMips32r6() ? Mips::SC_MMR6 : Mips::SC_MM; + BNE = STI->hasMips32r6() ? Mips::BNEC_MMR6 : Mips::BNE_MM; + BEQ = STI->hasMips32r6() ? Mips::BEQC_MMR6 : Mips::BEQ_MM; + } else { + LL = STI->hasMips32r6() + ? (ArePtrs64bit ? Mips::LL64_R6 : Mips::LL_R6) + : (ArePtrs64bit ? Mips::LL64 : Mips::LL); + SC = STI->hasMips32r6() + ? (ArePtrs64bit ? Mips::SC64_R6 : Mips::SC_R6) + : (ArePtrs64bit ? Mips::SC64 : Mips::SC); + BNE = Mips::BNE; + BEQ = Mips::BEQ; + } + + ZERO = Mips::ZERO; + MOVE = Mips::OR; + } else { + LL = STI->hasMips64r6() ? Mips::LLD_R6 : Mips::LLD; + SC = STI->hasMips64r6() ? Mips::SCD_R6 : Mips::SCD; + ZERO = Mips::ZERO_64; + BNE = Mips::BNE64; + BEQ = Mips::BEQ64; + MOVE = Mips::OR64; + } + + unsigned Dest = I->getOperand(0).getReg(); + unsigned Ptr = I->getOperand(1).getReg(); + unsigned OldVal = I->getOperand(2).getReg(); + unsigned NewVal = I->getOperand(3).getReg(); + unsigned Scratch = I->getOperand(4).getReg(); + + // insert new blocks after the current block + const BasicBlock *LLVM_BB = BB.getBasicBlock(); + MachineBasicBlock *loop1MBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *loop2MBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineFunction::iterator It = ++BB.getIterator(); + MF->insert(It, loop1MBB); + MF->insert(It, loop2MBB); + MF->insert(It, exitMBB); + + // Transfer the remainder of BB and its successor edges to exitMBB. + exitMBB->splice(exitMBB->begin(), &BB, + std::next(MachineBasicBlock::iterator(I)), BB.end()); + exitMBB->transferSuccessorsAndUpdatePHIs(&BB); + + // thisMBB: + // ... + // fallthrough --> loop1MBB + BB.addSuccessor(loop1MBB, BranchProbability::getOne()); + loop1MBB->addSuccessor(exitMBB); + loop1MBB->addSuccessor(loop2MBB); + loop1MBB->normalizeSuccProbs(); + loop2MBB->addSuccessor(loop1MBB); + loop2MBB->addSuccessor(exitMBB); + loop2MBB->normalizeSuccProbs(); + + // loop1MBB: + // ll dest, 0(ptr) + // bne dest, oldval, exitMBB + BuildMI(loop1MBB, DL, TII->get(LL), Dest).addReg(Ptr).addImm(0); + BuildMI(loop1MBB, DL, TII->get(BNE)) + .addReg(Dest, RegState::Kill).addReg(OldVal).addMBB(exitMBB); + + // loop2MBB: + // move scratch, NewVal + // sc Scratch, Scratch, 0(ptr) + // beq Scratch, $0, loop1MBB + BuildMI(loop2MBB, DL, TII->get(MOVE), Scratch).addReg(NewVal).addReg(ZERO); + BuildMI(loop2MBB, DL, TII->get(SC), Scratch) + .addReg(Scratch).addReg(Ptr).addImm(0); + BuildMI(loop2MBB, DL, TII->get(BEQ)) + .addReg(Scratch, RegState::Kill).addReg(ZERO).addMBB(loop1MBB); + + LivePhysRegs LiveRegs; + computeAndAddLiveIns(LiveRegs, *loop1MBB); + computeAndAddLiveIns(LiveRegs, *loop2MBB); + computeAndAddLiveIns(LiveRegs, *exitMBB); + + NMBBI = BB.end(); + I->eraseFromParent(); + return true; +} + +bool MipsExpandPseudo::expandAtomicBinOpSubword( + MachineBasicBlock &BB, MachineBasicBlock::iterator I, + MachineBasicBlock::iterator &NMBBI) { + + MachineFunction *MF = BB.getParent(); + + const bool ArePtrs64bit = STI->getABI().ArePtrs64bit(); + DebugLoc DL = I->getDebugLoc(); + + unsigned LL, SC; + unsigned BEQ = Mips::BEQ; + unsigned SEOp = Mips::SEH; + + if (STI->inMicroMipsMode()) { + LL = STI->hasMips32r6() ? Mips::LL_MMR6 : Mips::LL_MM; + SC = STI->hasMips32r6() ? Mips::SC_MMR6 : Mips::SC_MM; + BEQ = STI->hasMips32r6() ? Mips::BEQC_MMR6 : Mips::BEQ_MM; + } else { + LL = STI->hasMips32r6() ? (ArePtrs64bit ? Mips::LL64_R6 : Mips::LL_R6) + : (ArePtrs64bit ? Mips::LL64 : Mips::LL); + SC = STI->hasMips32r6() ? (ArePtrs64bit ? Mips::SC64_R6 : Mips::SC_R6) + : (ArePtrs64bit ? Mips::SC64 : Mips::SC); + } + + bool IsSwap = false; + bool IsNand = false; + + unsigned Opcode = 0; + switch (I->getOpcode()) { + case Mips::ATOMIC_LOAD_NAND_I8_POSTRA: + SEOp = Mips::SEB; + LLVM_FALLTHROUGH; + case Mips::ATOMIC_LOAD_NAND_I16_POSTRA: + IsNand = true; + break; + case Mips::ATOMIC_SWAP_I8_POSTRA: + SEOp = Mips::SEB; + LLVM_FALLTHROUGH; + case Mips::ATOMIC_SWAP_I16_POSTRA: + IsSwap = true; + break; + case Mips::ATOMIC_LOAD_ADD_I8_POSTRA: + SEOp = Mips::SEB; + LLVM_FALLTHROUGH; + case Mips::ATOMIC_LOAD_ADD_I16_POSTRA: + Opcode = Mips::ADDu; + break; + case Mips::ATOMIC_LOAD_SUB_I8_POSTRA: + SEOp = Mips::SEB; + LLVM_FALLTHROUGH; + case Mips::ATOMIC_LOAD_SUB_I16_POSTRA: + Opcode = Mips::SUBu; + break; + case Mips::ATOMIC_LOAD_AND_I8_POSTRA: + SEOp = Mips::SEB; + LLVM_FALLTHROUGH; + case Mips::ATOMIC_LOAD_AND_I16_POSTRA: + Opcode = Mips::AND; + break; + case Mips::ATOMIC_LOAD_OR_I8_POSTRA: + SEOp = Mips::SEB; + LLVM_FALLTHROUGH; + case Mips::ATOMIC_LOAD_OR_I16_POSTRA: + Opcode = Mips::OR; + break; + case Mips::ATOMIC_LOAD_XOR_I8_POSTRA: + SEOp = Mips::SEB; + LLVM_FALLTHROUGH; + case Mips::ATOMIC_LOAD_XOR_I16_POSTRA: + Opcode = Mips::XOR; + break; + default: + llvm_unreachable("Unknown subword atomic pseudo for expansion!"); + } + + unsigned Dest = I->getOperand(0).getReg(); + unsigned Ptr = I->getOperand(1).getReg(); + unsigned Incr = I->getOperand(2).getReg(); + unsigned Mask = I->getOperand(3).getReg(); + unsigned Mask2 = I->getOperand(4).getReg(); + unsigned ShiftAmnt = I->getOperand(5).getReg(); + unsigned OldVal = I->getOperand(6).getReg(); + unsigned BinOpRes = I->getOperand(7).getReg(); + unsigned StoreVal = I->getOperand(8).getReg(); + + const BasicBlock *LLVM_BB = BB.getBasicBlock(); + MachineBasicBlock *loopMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *sinkMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineFunction::iterator It = ++BB.getIterator(); + MF->insert(It, loopMBB); + MF->insert(It, sinkMBB); + MF->insert(It, exitMBB); + + exitMBB->splice(exitMBB->begin(), &BB, std::next(I), BB.end()); + exitMBB->transferSuccessorsAndUpdatePHIs(&BB); + + BB.addSuccessor(loopMBB, BranchProbability::getOne()); + loopMBB->addSuccessor(sinkMBB); + loopMBB->addSuccessor(loopMBB); + loopMBB->normalizeSuccProbs(); + + BuildMI(loopMBB, DL, TII->get(LL), OldVal).addReg(Ptr).addImm(0); + if (IsNand) { + // and andres, oldval, incr2 + // nor binopres, $0, andres + // and newval, binopres, mask + BuildMI(loopMBB, DL, TII->get(Mips::AND), BinOpRes) + .addReg(OldVal) + .addReg(Incr); + BuildMI(loopMBB, DL, TII->get(Mips::NOR), BinOpRes) + .addReg(Mips::ZERO) + .addReg(BinOpRes); + BuildMI(loopMBB, DL, TII->get(Mips::AND), BinOpRes) + .addReg(BinOpRes) + .addReg(Mask); + } else if (!IsSwap) { + // binopres, oldval, incr2 + // and newval, binopres, mask + BuildMI(loopMBB, DL, TII->get(Opcode), BinOpRes) + .addReg(OldVal) + .addReg(Incr); + BuildMI(loopMBB, DL, TII->get(Mips::AND), BinOpRes) + .addReg(BinOpRes) + .addReg(Mask); + } else { // atomic.swap + // and newval, incr2, mask + BuildMI(loopMBB, DL, TII->get(Mips::AND), BinOpRes) + .addReg(Incr) + .addReg(Mask); + } + + // and StoreVal, OlddVal, Mask2 + // or StoreVal, StoreVal, BinOpRes + // StoreVal = sc StoreVal, 0(Ptr) + // beq StoreVal, zero, loopMBB + BuildMI(loopMBB, DL, TII->get(Mips::AND), StoreVal) + .addReg(OldVal).addReg(Mask2); + BuildMI(loopMBB, DL, TII->get(Mips::OR), StoreVal) + .addReg(StoreVal).addReg(BinOpRes); + BuildMI(loopMBB, DL, TII->get(SC), StoreVal) + .addReg(StoreVal).addReg(Ptr).addImm(0); + BuildMI(loopMBB, DL, TII->get(BEQ)) + .addReg(StoreVal).addReg(Mips::ZERO).addMBB(loopMBB); + + // sinkMBB: + // and maskedoldval1,oldval,mask + // srl srlres,maskedoldval1,shiftamt + // sign_extend dest,srlres + + sinkMBB->addSuccessor(exitMBB, BranchProbability::getOne()); + + BuildMI(sinkMBB, DL, TII->get(Mips::AND), Dest) + .addReg(OldVal).addReg(Mask); + BuildMI(sinkMBB, DL, TII->get(Mips::SRLV), Dest) + .addReg(Dest).addReg(ShiftAmnt); + + if (STI->hasMips32r2()) { + BuildMI(sinkMBB, DL, TII->get(SEOp), Dest).addReg(Dest); + } else { + const unsigned ShiftImm = SEOp == Mips::SEH ? 16 : 24; + BuildMI(sinkMBB, DL, TII->get(Mips::SLL), Dest) + .addReg(Dest, RegState::Kill) + .addImm(ShiftImm); + BuildMI(sinkMBB, DL, TII->get(Mips::SRA), Dest) + .addReg(Dest, RegState::Kill) + .addImm(ShiftImm); + } + + LivePhysRegs LiveRegs; + computeAndAddLiveIns(LiveRegs, *loopMBB); + computeAndAddLiveIns(LiveRegs, *sinkMBB); + computeAndAddLiveIns(LiveRegs, *exitMBB); + + NMBBI = BB.end(); + I->eraseFromParent(); + + return true; +} + +bool MipsExpandPseudo::expandAtomicBinOp(MachineBasicBlock &BB, + MachineBasicBlock::iterator I, + MachineBasicBlock::iterator &NMBBI, + unsigned Size) { + MachineFunction *MF = BB.getParent(); + + const bool ArePtrs64bit = STI->getABI().ArePtrs64bit(); + DebugLoc DL = I->getDebugLoc(); + + unsigned LL, SC, ZERO, BEQ; + + if (Size == 4) { + if (STI->inMicroMipsMode()) { + LL = STI->hasMips32r6() ? Mips::LL_MMR6 : Mips::LL_MM; + SC = STI->hasMips32r6() ? Mips::SC_MMR6 : Mips::SC_MM; + BEQ = STI->hasMips32r6() ? Mips::BEQC_MMR6 : Mips::BEQ_MM; + } else { + LL = STI->hasMips32r6() + ? (ArePtrs64bit ? Mips::LL64_R6 : Mips::LL_R6) + : (ArePtrs64bit ? Mips::LL64 : Mips::LL); + SC = STI->hasMips32r6() + ? (ArePtrs64bit ? Mips::SC64_R6 : Mips::SC_R6) + : (ArePtrs64bit ? Mips::SC64 : Mips::SC); + BEQ = Mips::BEQ; + } + + ZERO = Mips::ZERO; + } else { + LL = STI->hasMips64r6() ? Mips::LLD_R6 : Mips::LLD; + SC = STI->hasMips64r6() ? Mips::SCD_R6 : Mips::SCD; + ZERO = Mips::ZERO_64; + BEQ = Mips::BEQ64; + } + + unsigned OldVal = I->getOperand(0).getReg(); + unsigned Ptr = I->getOperand(1).getReg(); + unsigned Incr = I->getOperand(2).getReg(); + unsigned Scratch = I->getOperand(3).getReg(); + + unsigned Opcode = 0; + unsigned OR = 0; + unsigned AND = 0; + unsigned NOR = 0; + bool IsNand = false; + switch (I->getOpcode()) { + case Mips::ATOMIC_LOAD_ADD_I32_POSTRA: + Opcode = Mips::ADDu; + break; + case Mips::ATOMIC_LOAD_SUB_I32_POSTRA: + Opcode = Mips::SUBu; + break; + case Mips::ATOMIC_LOAD_AND_I32_POSTRA: + Opcode = Mips::AND; + break; + case Mips::ATOMIC_LOAD_OR_I32_POSTRA: + Opcode = Mips::OR; + break; + case Mips::ATOMIC_LOAD_XOR_I32_POSTRA: + Opcode = Mips::XOR; + break; + case Mips::ATOMIC_LOAD_NAND_I32_POSTRA: + IsNand = true; + AND = Mips::AND; + NOR = Mips::NOR; + break; + case Mips::ATOMIC_SWAP_I32_POSTRA: + OR = Mips::OR; + break; + case Mips::ATOMIC_LOAD_ADD_I64_POSTRA: + Opcode = Mips::DADDu; + break; + case Mips::ATOMIC_LOAD_SUB_I64_POSTRA: + Opcode = Mips::DSUBu; + break; + case Mips::ATOMIC_LOAD_AND_I64_POSTRA: + Opcode = Mips::AND64; + break; + case Mips::ATOMIC_LOAD_OR_I64_POSTRA: + Opcode = Mips::OR64; + break; + case Mips::ATOMIC_LOAD_XOR_I64_POSTRA: + Opcode = Mips::XOR64; + break; + case Mips::ATOMIC_LOAD_NAND_I64_POSTRA: + IsNand = true; + AND = Mips::AND64; + NOR = Mips::NOR64; + break; + case Mips::ATOMIC_SWAP_I64_POSTRA: + OR = Mips::OR64; + break; + default: + llvm_unreachable("Unknown pseudo atomic!"); + } + + const BasicBlock *LLVM_BB = BB.getBasicBlock(); + MachineBasicBlock *loopMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); + MachineFunction::iterator It = ++BB.getIterator(); + MF->insert(It, loopMBB); + MF->insert(It, exitMBB); + + exitMBB->splice(exitMBB->begin(), &BB, std::next(I), BB.end()); + exitMBB->transferSuccessorsAndUpdatePHIs(&BB); + + BB.addSuccessor(loopMBB, BranchProbability::getOne()); + loopMBB->addSuccessor(exitMBB); + loopMBB->addSuccessor(loopMBB); + loopMBB->normalizeSuccProbs(); + + BuildMI(loopMBB, DL, TII->get(LL), OldVal).addReg(Ptr).addImm(0); + assert((OldVal != Ptr) && "Clobbered the wrong ptr reg!"); + assert((OldVal != Incr) && "Clobbered the wrong reg!"); + if (Opcode) { + BuildMI(loopMBB, DL, TII->get(Opcode), Scratch).addReg(OldVal).addReg(Incr); + } else if (IsNand) { + assert(AND && NOR && + "Unknown nand instruction for atomic pseudo expansion"); + BuildMI(loopMBB, DL, TII->get(AND), Scratch).addReg(OldVal).addReg(Incr); + BuildMI(loopMBB, DL, TII->get(NOR), Scratch).addReg(ZERO).addReg(Scratch); + } else { + assert(OR && "Unknown instruction for atomic pseudo expansion!"); + BuildMI(loopMBB, DL, TII->get(OR), Scratch).addReg(Incr).addReg(ZERO); + } + + BuildMI(loopMBB, DL, TII->get(SC), Scratch).addReg(Scratch).addReg(Ptr).addImm(0); + BuildMI(loopMBB, DL, TII->get(BEQ)).addReg(Scratch).addReg(ZERO).addMBB(loopMBB); + + NMBBI = BB.end(); + I->eraseFromParent(); + + LivePhysRegs LiveRegs; + computeAndAddLiveIns(LiveRegs, *loopMBB); + computeAndAddLiveIns(LiveRegs, *exitMBB); + + return true; +} + +bool MipsExpandPseudo::expandMI(MachineBasicBlock &MBB, + MachineBasicBlock::iterator MBBI, + MachineBasicBlock::iterator &NMBB) { + + bool Modified = false; + + switch (MBBI->getOpcode()) { + case Mips::ATOMIC_CMP_SWAP_I32_POSTRA: + case Mips::ATOMIC_CMP_SWAP_I64_POSTRA: + return expandAtomicCmpSwap(MBB, MBBI, NMBB); + case Mips::ATOMIC_CMP_SWAP_I8_POSTRA: + case Mips::ATOMIC_CMP_SWAP_I16_POSTRA: + return expandAtomicCmpSwapSubword(MBB, MBBI, NMBB); + case Mips::ATOMIC_SWAP_I8_POSTRA: + case Mips::ATOMIC_SWAP_I16_POSTRA: + case Mips::ATOMIC_LOAD_NAND_I8_POSTRA: + case Mips::ATOMIC_LOAD_NAND_I16_POSTRA: + case Mips::ATOMIC_LOAD_ADD_I8_POSTRA: + case Mips::ATOMIC_LOAD_ADD_I16_POSTRA: + case Mips::ATOMIC_LOAD_SUB_I8_POSTRA: + case Mips::ATOMIC_LOAD_SUB_I16_POSTRA: + case Mips::ATOMIC_LOAD_AND_I8_POSTRA: + case Mips::ATOMIC_LOAD_AND_I16_POSTRA: + case Mips::ATOMIC_LOAD_OR_I8_POSTRA: + case Mips::ATOMIC_LOAD_OR_I16_POSTRA: + case Mips::ATOMIC_LOAD_XOR_I8_POSTRA: + case Mips::ATOMIC_LOAD_XOR_I16_POSTRA: + return expandAtomicBinOpSubword(MBB, MBBI, NMBB); + case Mips::ATOMIC_LOAD_ADD_I32_POSTRA: + case Mips::ATOMIC_LOAD_SUB_I32_POSTRA: + case Mips::ATOMIC_LOAD_AND_I32_POSTRA: + case Mips::ATOMIC_LOAD_OR_I32_POSTRA: + case Mips::ATOMIC_LOAD_XOR_I32_POSTRA: + case Mips::ATOMIC_LOAD_NAND_I32_POSTRA: + case Mips::ATOMIC_SWAP_I32_POSTRA: + return expandAtomicBinOp(MBB, MBBI, NMBB, 4); + case Mips::ATOMIC_LOAD_ADD_I64_POSTRA: + case Mips::ATOMIC_LOAD_SUB_I64_POSTRA: + case Mips::ATOMIC_LOAD_AND_I64_POSTRA: + case Mips::ATOMIC_LOAD_OR_I64_POSTRA: + case Mips::ATOMIC_LOAD_XOR_I64_POSTRA: + case Mips::ATOMIC_LOAD_NAND_I64_POSTRA: + case Mips::ATOMIC_SWAP_I64_POSTRA: + return expandAtomicBinOp(MBB, MBBI, NMBB, 8); + default: + return Modified; + } +} + +bool MipsExpandPseudo::expandMBB(MachineBasicBlock &MBB) { + bool Modified = false; + + MachineBasicBlock::iterator MBBI = MBB.begin(), E = MBB.end(); + while (MBBI != E) { + MachineBasicBlock::iterator NMBBI = std::next(MBBI); + Modified |= expandMI(MBB, MBBI, NMBBI); + MBBI = NMBBI; + } + + return Modified; +} + +bool MipsExpandPseudo::runOnMachineFunction(MachineFunction &MF) { + STI = &static_cast(MF.getSubtarget()); + TII = STI->getInstrInfo(); + + bool Modified = false; + for (MachineFunction::iterator MFI = MF.begin(), E = MF.end(); MFI != E; + ++MFI) + Modified |= expandMBB(*MFI); + + if (Modified) + MF.RenumberBlocks(); + + return Modified; +} + +/// createMipsExpandPseudoPass - returns an instance of the pseudo instruction +/// expansion pass. +FunctionPass *llvm::createMipsExpandPseudoPass() { + return new MipsExpandPseudo(); +} Index: llvm/trunk/lib/Target/Mips/Mips.h =================================================================== --- llvm/trunk/lib/Target/Mips/Mips.h (revision 336327) +++ llvm/trunk/lib/Target/Mips/Mips.h (revision 336328) @@ -1,50 +1,51 @@ //===-- Mips.h - Top-level interface for Mips representation ----*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file contains the entry points for global functions defined in // the LLVM Mips back-end. // //===----------------------------------------------------------------------===// #ifndef LLVM_LIB_TARGET_MIPS_MIPS_H #define LLVM_LIB_TARGET_MIPS_MIPS_H #include "MCTargetDesc/MipsMCTargetDesc.h" #include "llvm/Target/TargetMachine.h" namespace llvm { class MipsTargetMachine; class ModulePass; class FunctionPass; class MipsRegisterBankInfo; class MipsSubtarget; class MipsTargetMachine; class InstructionSelector; class PassRegistry; ModulePass *createMipsOs16Pass(); ModulePass *createMips16HardFloatPass(); FunctionPass *createMipsModuleISelDagPass(); FunctionPass *createMipsOptimizePICCallPass(); FunctionPass *createMipsDelaySlotFillerPass(); FunctionPass *createMipsBranchExpansion(); FunctionPass *createMipsConstantIslandPass(); FunctionPass *createMicroMipsSizeReducePass(); + FunctionPass *createMipsExpandPseudoPass(); InstructionSelector *createMipsInstructionSelector(const MipsTargetMachine &, MipsSubtarget &, MipsRegisterBankInfo &); void initializeMipsDelaySlotFillerPass(PassRegistry &); void initializeMipsBranchExpansionPass(PassRegistry &); void initializeMicroMipsSizeReducePass(PassRegistry &); } // end namespace llvm; #endif Index: llvm/trunk/lib/Target/Mips/CMakeLists.txt =================================================================== --- llvm/trunk/lib/Target/Mips/CMakeLists.txt (revision 336327) +++ llvm/trunk/lib/Target/Mips/CMakeLists.txt (revision 336328) @@ -1,63 +1,64 @@ set(LLVM_TARGET_DEFINITIONS Mips.td) tablegen(LLVM MipsGenAsmMatcher.inc -gen-asm-matcher) tablegen(LLVM MipsGenAsmWriter.inc -gen-asm-writer) tablegen(LLVM MipsGenCallingConv.inc -gen-callingconv) tablegen(LLVM MipsGenDAGISel.inc -gen-dag-isel) tablegen(LLVM MipsGenDisassemblerTables.inc -gen-disassembler) tablegen(LLVM MipsGenFastISel.inc -gen-fast-isel) tablegen(LLVM MipsGenGlobalISel.inc -gen-global-isel) tablegen(LLVM MipsGenInstrInfo.inc -gen-instr-info) tablegen(LLVM MipsGenMCCodeEmitter.inc -gen-emitter) tablegen(LLVM MipsGenMCPseudoLowering.inc -gen-pseudo-lowering) tablegen(LLVM MipsGenRegisterBank.inc -gen-register-bank) tablegen(LLVM MipsGenRegisterInfo.inc -gen-register-info) tablegen(LLVM MipsGenSubtargetInfo.inc -gen-subtarget) add_public_tablegen_target(MipsCommonTableGen) add_llvm_target(MipsCodeGen Mips16FrameLowering.cpp Mips16HardFloat.cpp Mips16HardFloatInfo.cpp Mips16InstrInfo.cpp Mips16ISelDAGToDAG.cpp Mips16ISelLowering.cpp Mips16RegisterInfo.cpp MipsAnalyzeImmediate.cpp MipsAsmPrinter.cpp MipsCallLowering.cpp MipsCCState.cpp MipsConstantIslandPass.cpp MipsDelaySlotFiller.cpp + MipsExpandPseudo.cpp MipsFastISel.cpp MipsInstrInfo.cpp MipsInstructionSelector.cpp MipsISelDAGToDAG.cpp MipsISelLowering.cpp MipsFrameLowering.cpp MipsLegalizerInfo.cpp MipsBranchExpansion.cpp MipsMCInstLower.cpp MipsMachineFunction.cpp MipsModuleISelDAGToDAG.cpp MipsOptimizePICCall.cpp MipsOs16.cpp MipsRegisterBankInfo.cpp MipsRegisterInfo.cpp MipsSEFrameLowering.cpp MipsSEInstrInfo.cpp MipsSEISelDAGToDAG.cpp MipsSEISelLowering.cpp MipsSERegisterInfo.cpp MipsSubtarget.cpp MipsTargetMachine.cpp MipsTargetObjectFile.cpp MicroMipsSizeReduction.cpp ) add_subdirectory(AsmParser) add_subdirectory(Disassembler) add_subdirectory(InstPrinter) add_subdirectory(MCTargetDesc) add_subdirectory(TargetInfo) Index: llvm/trunk/lib/Target/Mips/Mips64InstrInfo.td =================================================================== --- llvm/trunk/lib/Target/Mips/Mips64InstrInfo.td (revision 336327) +++ llvm/trunk/lib/Target/Mips/Mips64InstrInfo.td (revision 336328) @@ -1,1038 +1,1049 @@ //===- Mips64InstrInfo.td - Mips64 Instruction Information -*- tablegen -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file describes Mips64 instructions. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // Mips Operand, Complex Patterns and Transformations Definitions. //===----------------------------------------------------------------------===// // shamt must fit in 6 bits. def immZExt6 : ImmLeaf; // Node immediate fits as 10-bit sign extended on target immediate. // e.g. seqi, snei def immSExt10_64 : PatLeaf<(i64 imm), [{ return isInt<10>(N->getSExtValue()); }]>; def immZExt16_64 : PatLeaf<(i64 imm), [{ return isUInt<16>(N->getZExtValue()); }]>; def immZExt5_64 : ImmLeaf; // Transformation function: get log2 of low 32 bits of immediate def Log2LO : SDNodeXFormgetZExtValue())); }]>; // Transformation function: get log2 of high 32 bits of immediate def Log2HI : SDNodeXFormgetZExtValue() >> 32))); }]>; // Predicate: True if immediate is a power of 2 and fits 32 bits def PowerOf2LO : PatLeaf<(imm), [{ if (N->getValueType(0) == MVT::i64) { uint64_t Imm = N->getZExtValue(); return isPowerOf2_64(Imm) && (Imm & 0xffffffff) == Imm; } else return false; }]>; // Predicate: True if immediate is a power of 2 and exceeds 32 bits def PowerOf2HI : PatLeaf<(imm), [{ if (N->getValueType(0) == MVT::i64) { uint64_t Imm = N->getZExtValue(); return isPowerOf2_64(Imm) && (Imm & 0xffffffff00000000) == Imm; } else return false; }]>; def PowerOf2LO_i32 : PatLeaf<(imm), [{ if (N->getValueType(0) == MVT::i32) { uint64_t Imm = N->getZExtValue(); return isPowerOf2_32(Imm) && isUInt<32>(Imm); } else return false; }]>; def assertzext_lt_i32 : PatFrag<(ops node:$src), (assertzext node:$src), [{ return cast(N->getOperand(1))->getVT().bitsLT(MVT::i32); }]>; //===----------------------------------------------------------------------===// // Instructions specific format //===----------------------------------------------------------------------===// let usesCustomInserter = 1 in { def ATOMIC_LOAD_ADD_I64 : Atomic2Ops; def ATOMIC_LOAD_SUB_I64 : Atomic2Ops; def ATOMIC_LOAD_AND_I64 : Atomic2Ops; def ATOMIC_LOAD_OR_I64 : Atomic2Ops; def ATOMIC_LOAD_XOR_I64 : Atomic2Ops; def ATOMIC_LOAD_NAND_I64 : Atomic2Ops; def ATOMIC_SWAP_I64 : Atomic2Ops; def ATOMIC_CMP_SWAP_I64 : AtomicCmpSwap; } +def ATOMIC_LOAD_ADD_I64_POSTRA : Atomic2OpsPostRA; +def ATOMIC_LOAD_SUB_I64_POSTRA : Atomic2OpsPostRA; +def ATOMIC_LOAD_AND_I64_POSTRA : Atomic2OpsPostRA; +def ATOMIC_LOAD_OR_I64_POSTRA : Atomic2OpsPostRA; +def ATOMIC_LOAD_XOR_I64_POSTRA : Atomic2OpsPostRA; +def ATOMIC_LOAD_NAND_I64_POSTRA : Atomic2OpsPostRA; + +def ATOMIC_SWAP_I64_POSTRA : Atomic2OpsPostRA; + +def ATOMIC_CMP_SWAP_I64_POSTRA : AtomicCmpSwapPostRA; + /// Pseudo instructions for loading and storing accumulator registers. let isPseudo = 1, isCodeGenOnly = 1, hasNoSchedulingInfo = 1 in { def LOAD_ACC128 : Load<"", ACC128>; def STORE_ACC128 : Store<"", ACC128>; } //===----------------------------------------------------------------------===// // Instruction definition //===----------------------------------------------------------------------===// let DecoderNamespace = "Mips64" in { /// Arithmetic Instructions (ALU Immediate) def DADDi : ArithLogicI<"daddi", simm16_64, GPR64Opnd, II_DADDI>, ADDI_FM<0x18>, ISA_MIPS3_NOT_32R6_64R6; let AdditionalPredicates = [NotInMicroMips] in { def DADDiu : ArithLogicI<"daddiu", simm16_64, GPR64Opnd, II_DADDIU, immSExt16, add>, ADDI_FM<0x19>, IsAsCheapAsAMove, ISA_MIPS3; } let isCodeGenOnly = 1 in { def SLTi64 : SetCC_I<"slti", setlt, simm16_64, immSExt16, GPR64Opnd>, SLTI_FM<0xa>, GPR_64; def SLTiu64 : SetCC_I<"sltiu", setult, simm16_64, immSExt16, GPR64Opnd>, SLTI_FM<0xb>, GPR_64; def ANDi64 : ArithLogicI<"andi", uimm16_64, GPR64Opnd, II_AND, immZExt16, and>, ADDI_FM<0xc>, GPR_64; def ORi64 : ArithLogicI<"ori", uimm16_64, GPR64Opnd, II_OR, immZExt16, or>, ADDI_FM<0xd>, GPR_64; def XORi64 : ArithLogicI<"xori", uimm16_64, GPR64Opnd, II_XOR, immZExt16, xor>, ADDI_FM<0xe>, GPR_64; def LUi64 : LoadUpper<"lui", GPR64Opnd, uimm16_64_relaxed>, LUI_FM, GPR_64; } /// Arithmetic Instructions (3-Operand, R-Type) let AdditionalPredicates = [NotInMicroMips] in { def DADD : ArithLogicR<"dadd", GPR64Opnd, 1, II_DADD>, ADD_FM<0, 0x2c>, ISA_MIPS3; def DADDu : ArithLogicR<"daddu", GPR64Opnd, 1, II_DADDU, add>, ADD_FM<0, 0x2d>, ISA_MIPS3; def DSUBu : ArithLogicR<"dsubu", GPR64Opnd, 0, II_DSUBU, sub>, ADD_FM<0, 0x2f>, ISA_MIPS3; def DSUB : ArithLogicR<"dsub", GPR64Opnd, 0, II_DSUB>, ADD_FM<0, 0x2e>, ISA_MIPS3; } let isCodeGenOnly = 1 in { def SLT64 : SetCC_R<"slt", setlt, GPR64Opnd>, ADD_FM<0, 0x2a>, GPR_64; def SLTu64 : SetCC_R<"sltu", setult, GPR64Opnd>, ADD_FM<0, 0x2b>, GPR_64; def AND64 : ArithLogicR<"and", GPR64Opnd, 1, II_AND, and>, ADD_FM<0, 0x24>, GPR_64; def OR64 : ArithLogicR<"or", GPR64Opnd, 1, II_OR, or>, ADD_FM<0, 0x25>, GPR_64; def XOR64 : ArithLogicR<"xor", GPR64Opnd, 1, II_XOR, xor>, ADD_FM<0, 0x26>, GPR_64; def NOR64 : LogicNOR<"nor", GPR64Opnd>, ADD_FM<0, 0x27>, GPR_64; } /// Shift Instructions let AdditionalPredicates = [NotInMicroMips] in { def DSLL : shift_rotate_imm<"dsll", uimm6, GPR64Opnd, II_DSLL, shl, immZExt6>, SRA_FM<0x38, 0>, ISA_MIPS3; def DSRL : shift_rotate_imm<"dsrl", uimm6, GPR64Opnd, II_DSRL, srl, immZExt6>, SRA_FM<0x3a, 0>, ISA_MIPS3; def DSRA : shift_rotate_imm<"dsra", uimm6, GPR64Opnd, II_DSRA, sra, immZExt6>, SRA_FM<0x3b, 0>, ISA_MIPS3; def DSLLV : shift_rotate_reg<"dsllv", GPR64Opnd, II_DSLLV, shl>, SRLV_FM<0x14, 0>, ISA_MIPS3; def DSRAV : shift_rotate_reg<"dsrav", GPR64Opnd, II_DSRAV, sra>, SRLV_FM<0x17, 0>, ISA_MIPS3; def DSRLV : shift_rotate_reg<"dsrlv", GPR64Opnd, II_DSRLV, srl>, SRLV_FM<0x16, 0>, ISA_MIPS3; def DSLL32 : shift_rotate_imm<"dsll32", uimm5, GPR64Opnd, II_DSLL32>, SRA_FM<0x3c, 0>, ISA_MIPS3; def DSRL32 : shift_rotate_imm<"dsrl32", uimm5, GPR64Opnd, II_DSRL32>, SRA_FM<0x3e, 0>, ISA_MIPS3; def DSRA32 : shift_rotate_imm<"dsra32", uimm5, GPR64Opnd, II_DSRA32>, SRA_FM<0x3f, 0>, ISA_MIPS3; // Rotate Instructions def DROTR : shift_rotate_imm<"drotr", uimm6, GPR64Opnd, II_DROTR, rotr, immZExt6>, SRA_FM<0x3a, 1>, ISA_MIPS64R2; def DROTRV : shift_rotate_reg<"drotrv", GPR64Opnd, II_DROTRV, rotr>, SRLV_FM<0x16, 1>, ISA_MIPS64R2; def DROTR32 : shift_rotate_imm<"drotr32", uimm5, GPR64Opnd, II_DROTR32>, SRA_FM<0x3e, 1>, ISA_MIPS64R2; } /// Load and Store Instructions /// aligned let isCodeGenOnly = 1 in { def LB64 : Load<"lb", GPR64Opnd, sextloadi8, II_LB>, LW_FM<0x20>, GPR_64; def LBu64 : Load<"lbu", GPR64Opnd, zextloadi8, II_LBU>, LW_FM<0x24>, GPR_64; def LH64 : Load<"lh", GPR64Opnd, sextloadi16, II_LH>, LW_FM<0x21>, GPR_64; def LHu64 : Load<"lhu", GPR64Opnd, zextloadi16, II_LHU>, LW_FM<0x25>, GPR_64; def LW64 : Load<"lw", GPR64Opnd, sextloadi32, II_LW>, LW_FM<0x23>, GPR_64; def SB64 : Store<"sb", GPR64Opnd, truncstorei8, II_SB>, LW_FM<0x28>, GPR_64; def SH64 : Store<"sh", GPR64Opnd, truncstorei16, II_SH>, LW_FM<0x29>, GPR_64; def SW64 : Store<"sw", GPR64Opnd, truncstorei32, II_SW>, LW_FM<0x2b>, GPR_64; } let AdditionalPredicates = [NotInMicroMips] in { def LWu : MMRel, Load<"lwu", GPR64Opnd, zextloadi32, II_LWU>, LW_FM<0x27>, ISA_MIPS3; def LD : LoadMemory<"ld", GPR64Opnd, mem_simmptr, load, II_LD>, LW_FM<0x37>, ISA_MIPS3; def SD : StoreMemory<"sd", GPR64Opnd, mem_simmptr, store, II_SD>, LW_FM<0x3f>, ISA_MIPS3; } /// load/store left/right let isCodeGenOnly = 1 in { def LWL64 : LoadLeftRight<"lwl", MipsLWL, GPR64Opnd, II_LWL>, LW_FM<0x22>, GPR_64; def LWR64 : LoadLeftRight<"lwr", MipsLWR, GPR64Opnd, II_LWR>, LW_FM<0x26>, GPR_64; def SWL64 : StoreLeftRight<"swl", MipsSWL, GPR64Opnd, II_SWL>, LW_FM<0x2a>, GPR_64; def SWR64 : StoreLeftRight<"swr", MipsSWR, GPR64Opnd, II_SWR>, LW_FM<0x2e>, GPR_64; } def LDL : LoadLeftRight<"ldl", MipsLDL, GPR64Opnd, II_LDL>, LW_FM<0x1a>, ISA_MIPS3_NOT_32R6_64R6; def LDR : LoadLeftRight<"ldr", MipsLDR, GPR64Opnd, II_LDR>, LW_FM<0x1b>, ISA_MIPS3_NOT_32R6_64R6; def SDL : StoreLeftRight<"sdl", MipsSDL, GPR64Opnd, II_SDL>, LW_FM<0x2c>, ISA_MIPS3_NOT_32R6_64R6; def SDR : StoreLeftRight<"sdr", MipsSDR, GPR64Opnd, II_SDR>, LW_FM<0x2d>, ISA_MIPS3_NOT_32R6_64R6; /// Load-linked, Store-conditional let AdditionalPredicates = [NotInMicroMips] in { def LLD : LLBase<"lld", GPR64Opnd, mem_simmptr>, LW_FM<0x34>, ISA_MIPS3_NOT_32R6_64R6; } def SCD : SCBase<"scd", GPR64Opnd>, LW_FM<0x3c>, ISA_MIPS3_NOT_32R6_64R6; let AdditionalPredicates = [NotInMicroMips], DecoderNamespace = "Mips32_64_PTR64" in { def LL64 : LLBase<"ll", GPR32Opnd>, LW_FM<0x30>, PTR_64, ISA_MIPS2_NOT_32R6_64R6; def SC64 : SCBase<"sc", GPR32Opnd>, LW_FM<0x38>, PTR_64, ISA_MIPS2_NOT_32R6_64R6; def JR64 : IndirectBranch<"jr", GPR64Opnd>, MTLO_FM<8>, PTR_64; } def JALR64 : JumpLinkReg<"jalr", GPR64Opnd>, JALR_FM; /// Jump and Branch Instructions let isCodeGenOnly = 1 in { def BEQ64 : CBranch<"beq", brtarget, seteq, GPR64Opnd>, BEQ_FM<4>, GPR_64; def BNE64 : CBranch<"bne", brtarget, setne, GPR64Opnd>, BEQ_FM<5>, GPR_64; def BGEZ64 : CBranchZero<"bgez", brtarget, setge, GPR64Opnd>, BGEZ_FM<1, 1>, GPR_64; def BGTZ64 : CBranchZero<"bgtz", brtarget, setgt, GPR64Opnd>, BGEZ_FM<7, 0>, GPR_64; def BLEZ64 : CBranchZero<"blez", brtarget, setle, GPR64Opnd>, BGEZ_FM<6, 0>, GPR_64; def BLTZ64 : CBranchZero<"bltz", brtarget, setlt, GPR64Opnd>, BGEZ_FM<1, 0>, GPR_64; let AdditionalPredicates = [NoIndirectJumpGuards] in def JALR64Pseudo : JumpLinkRegPseudo; } let AdditionalPredicates = [NotInMicroMips], DecoderNamespace = "Mips64" in { def JR_HB64 : JR_HB_DESC, JR_HB_ENC, ISA_MIPS32_NOT_32R6_64R6; def JALR_HB64 : JALR_HB_DESC, JALR_HB_ENC, ISA_MIPS32R2; } def PseudoReturn64 : PseudoReturnBase; let AdditionalPredicates = [NotInMips16Mode, NotInMicroMips, NoIndirectJumpGuards] in { def TAILCALLREG64 : TailCallReg, ISA_MIPS3_NOT_32R6_64R6, PTR_64; def PseudoIndirectBranch64 : PseudoIndirectBranchBase, ISA_MIPS3_NOT_32R6_64R6; } let AdditionalPredicates = [NotInMips16Mode, NotInMicroMips, UseIndirectJumpsHazard] in { def TAILCALLREGHB64 : TailCallReg, ISA_MIPS32R2_NOT_32R6_64R6, PTR_64; def PseudoIndirectHazardBranch64 : PseudoIndirectBranchBase, ISA_MIPS32R2_NOT_32R6_64R6; } /// Multiply and Divide Instructions. let AdditionalPredicates = [NotInMicroMips] in { def DMULT : Mult<"dmult", II_DMULT, GPR64Opnd, [HI0_64, LO0_64]>, MULT_FM<0, 0x1c>, ISA_MIPS3_NOT_32R6_64R6; def DMULTu : Mult<"dmultu", II_DMULTU, GPR64Opnd, [HI0_64, LO0_64]>, MULT_FM<0, 0x1d>, ISA_MIPS3_NOT_32R6_64R6; } def PseudoDMULT : MultDivPseudo, ISA_MIPS3_NOT_32R6_64R6; def PseudoDMULTu : MultDivPseudo, ISA_MIPS3_NOT_32R6_64R6; let AdditionalPredicates = [NotInMicroMips] in { def DSDIV : Div<"ddiv", II_DDIV, GPR64Opnd, [HI0_64, LO0_64]>, MULT_FM<0, 0x1e>, ISA_MIPS3_NOT_32R6_64R6; def DUDIV : Div<"ddivu", II_DDIVU, GPR64Opnd, [HI0_64, LO0_64]>, MULT_FM<0, 0x1f>, ISA_MIPS3_NOT_32R6_64R6; } def PseudoDSDIV : MultDivPseudo, ISA_MIPS3_NOT_32R6_64R6; def PseudoDUDIV : MultDivPseudo, ISA_MIPS3_NOT_32R6_64R6; let isCodeGenOnly = 1 in { def MTHI64 : MoveToLOHI<"mthi", GPR64Opnd, [HI0_64]>, MTLO_FM<0x11>, ISA_MIPS3_NOT_32R6_64R6; def MTLO64 : MoveToLOHI<"mtlo", GPR64Opnd, [LO0_64]>, MTLO_FM<0x13>, ISA_MIPS3_NOT_32R6_64R6; def MFHI64 : MoveFromLOHI<"mfhi", GPR64Opnd, AC0_64>, MFLO_FM<0x10>, ISA_MIPS3_NOT_32R6_64R6; def MFLO64 : MoveFromLOHI<"mflo", GPR64Opnd, AC0_64>, MFLO_FM<0x12>, ISA_MIPS3_NOT_32R6_64R6; def PseudoMFHI64 : PseudoMFLOHI, ISA_MIPS3_NOT_32R6_64R6; def PseudoMFLO64 : PseudoMFLOHI, ISA_MIPS3_NOT_32R6_64R6; def PseudoMTLOHI64 : PseudoMTLOHI, ISA_MIPS3_NOT_32R6_64R6; /// Sign Ext In Register Instructions. def SEB64 : SignExtInReg<"seb", i8, GPR64Opnd, II_SEB>, SEB_FM<0x10, 0x20>, ISA_MIPS32R2; def SEH64 : SignExtInReg<"seh", i16, GPR64Opnd, II_SEH>, SEB_FM<0x18, 0x20>, ISA_MIPS32R2; } /// Count Leading let AdditionalPredicates = [NotInMicroMips] in { def DCLZ : CountLeading0<"dclz", GPR64Opnd, II_DCLZ>, CLO_FM<0x24>, ISA_MIPS64_NOT_64R6; def DCLO : CountLeading1<"dclo", GPR64Opnd, II_DCLO>, CLO_FM<0x25>, ISA_MIPS64_NOT_64R6; /// Double Word Swap Bytes/HalfWords def DSBH : SubwordSwap<"dsbh", GPR64Opnd, II_DSBH>, SEB_FM<2, 0x24>, ISA_MIPS64R2; def DSHD : SubwordSwap<"dshd", GPR64Opnd, II_DSHD>, SEB_FM<5, 0x24>, ISA_MIPS64R2; def LEA_ADDiu64 : EffectiveAddress<"daddiu", GPR64Opnd>, LW_FM<0x19>, GPR_64; } let isCodeGenOnly = 1 in def RDHWR64 : ReadHardware, RDHWR_FM, GPR_64; let AdditionalPredicates = [NotInMicroMips] in { // The 'pos + size' constraints for code generation are enforced by the // code that lowers into MipsISD::Ext. // For assembly parsing, we alias dextu and dextm to dext, and match by // operand were possible then check the 'pos + size' in MipsAsmParser. // We override the generated decoder to enforce that dext always comes out // for dextm and dextu like binutils. let DecoderMethod = "DecodeDEXT" in { def DEXT : ExtBase<"dext", GPR64Opnd, uimm5_report_uimm6, uimm5_plus1_report_uimm6, immZExt5, immZExt5Plus1, MipsExt>, EXT_FM<3>, ISA_MIPS64R2; def DEXTM : ExtBase<"dextm", GPR64Opnd, uimm5, uimm5_plus33, immZExt5, immZExt5Plus33, MipsExt>, EXT_FM<1>, ISA_MIPS64R2; def DEXTU : ExtBase<"dextu", GPR64Opnd, uimm5_plus32, uimm5_plus1, immZExt5Plus32, immZExt5Plus1, MipsExt>, EXT_FM<2>, ISA_MIPS64R2; } // The 'pos + size' constraints for code generation are enforced by the // code that lowers into MipsISD::Ins. // For assembly parsing, we alias dinsu and dinsm to dins, and match by // operand were possible then check the 'pos + size' in MipsAsmParser. // We override the generated decoder to enforce that dins always comes out // for dinsm and dinsu like binutils. let DecoderMethod = "DecodeDINS" in { def DINS : InsBase<"dins", GPR64Opnd, uimm6, uimm5_inssize_plus1, immZExt5, immZExt5Plus1>, EXT_FM<7>, ISA_MIPS64R2; def DINSU : InsBase<"dinsu", GPR64Opnd, uimm5_plus32, uimm5_inssize_plus1, immZExt5Plus32, immZExt5Plus1>, EXT_FM<6>, ISA_MIPS64R2; def DINSM : InsBase<"dinsm", GPR64Opnd, uimm5, uimm_range_2_64, immZExt5, immZExtRange2To64>, EXT_FM<5>, ISA_MIPS64R2; } } let isCodeGenOnly = 1, AdditionalPredicates = [NotInMicroMips] in { def DEXT64_32 : InstSE<(outs GPR64Opnd:$rt), (ins GPR32Opnd:$rs, uimm5_report_uimm6:$pos, uimm5_plus1:$size), "dext $rt, $rs, $pos, $size", [], II_EXT, FrmR, "dext">, EXT_FM<3>, ISA_MIPS64R2; } let isCodeGenOnly = 1, rs = 0, shamt = 0 in { def DSLL64_32 : FR<0x00, 0x3c, (outs GPR64:$rd), (ins GPR32:$rt), "dsll\t$rd, $rt, 32", [], II_DSLL>, GPR_64; let isMoveReg = 1 in { def SLL64_32 : FR<0x0, 0x00, (outs GPR64:$rd), (ins GPR32:$rt), "sll\t$rd, $rt, 0", [], II_SLL>, GPR_64; def SLL64_64 : FR<0x0, 0x00, (outs GPR64:$rd), (ins GPR64:$rt), "sll\t$rd, $rt, 0", [], II_SLL>, GPR_64; } } // We need the following pseudo instruction to avoid offset calculation for // long branches. See the comment in file MipsLongBranch.cpp for detailed // explanation. // Expands to: daddiu $dst, $src, %PART($tgt - $baltgt) // where %PART may be %hi or %lo, depending on the relocation kind // that $tgt is annotated with. def LONG_BRANCH_DADDiu : PseudoSE<(outs GPR64Opnd:$dst), (ins GPR64Opnd:$src, brtarget:$tgt, brtarget:$baltgt), []>, GPR_64; // Cavium Octeon cnMIPS instructions let DecoderNamespace = "CnMips", // FIXME: The lack of HasStdEnc is probably a bug EncodingPredicates = [] in { class Count1s: InstSE<(outs RO:$rd), (ins RO:$rs), !strconcat(opstr, "\t$rd, $rs"), [(set RO:$rd, (ctpop RO:$rs))], II_POP, FrmR, opstr> { let TwoOperandAliasConstraint = "$rd = $rs"; } class ExtsCins: InstSE<(outs RO:$rt), (ins RO:$rs, uimm5:$pos, uimm5:$lenm1), !strconcat(opstr, "\t$rt, $rs, $pos, $lenm1"), [(set RO:$rt, (Op RO:$rs, PosImm:$pos, imm:$lenm1))], itin, FrmR, opstr> { let TwoOperandAliasConstraint = "$rt = $rs"; } class SetCC64_R : InstSE<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, GPR64Opnd:$rt), !strconcat(opstr, "\t$rd, $rs, $rt"), [(set GPR64Opnd:$rd, (zext (cond_op GPR64Opnd:$rs, GPR64Opnd:$rt)))], II_SEQ_SNE, FrmR, opstr> { let TwoOperandAliasConstraint = "$rd = $rs"; } class SetCC64_I: InstSE<(outs GPR64Opnd:$rt), (ins GPR64Opnd:$rs, simm10_64:$imm10), !strconcat(opstr, "\t$rt, $rs, $imm10"), [(set GPR64Opnd:$rt, (zext (cond_op GPR64Opnd:$rs, immSExt10_64:$imm10)))], II_SEQI_SNEI, FrmI, opstr> { let TwoOperandAliasConstraint = "$rt = $rs"; } class CBranchBitNum shift = 1> : InstSE<(outs), (ins RO:$rs, ImmOp:$p, opnd:$offset), !strconcat(opstr, "\t$rs, $p, $offset"), [(brcond (i32 (cond_op (and RO:$rs, (shl shift, immZExt5_64:$p)), 0)), bb:$offset)], II_BBIT, FrmI, opstr> { let isBranch = 1; let isTerminator = 1; let hasDelaySlot = 1; let Defs = [AT]; } class MFC2OP : InstSE<(outs RO:$rt, uimm16:$imm16), (ins), !strconcat(asmstr, "\t$rt, $imm16"), [], itin, FrmFR>; // Unsigned Byte Add def BADDu : ArithLogicR<"baddu", GPR64Opnd, 1, II_BADDU>, ADD_FM<0x1c, 0x28>, ASE_CNMIPS { let Pattern = [(set GPR64Opnd:$rd, (and (add GPR64Opnd:$rs, GPR64Opnd:$rt), 255))]; } // Branch on Bit Clear /+32 def BBIT0 : CBranchBitNum<"bbit0", brtarget, seteq, GPR64Opnd, uimm5_64_report_uimm6>, BBIT_FM<0x32>, ASE_CNMIPS; def BBIT032: CBranchBitNum<"bbit032", brtarget, seteq, GPR64Opnd, uimm5_64, 0x100000000>, BBIT_FM<0x36>, ASE_CNMIPS; // Branch on Bit Set /+32 def BBIT1 : CBranchBitNum<"bbit1", brtarget, setne, GPR64Opnd, uimm5_64_report_uimm6>, BBIT_FM<0x3a>, ASE_CNMIPS; def BBIT132: CBranchBitNum<"bbit132", brtarget, setne, GPR64Opnd, uimm5_64, 0x100000000>, BBIT_FM<0x3e>, ASE_CNMIPS; // Multiply Doubleword to GPR def DMUL : ArithLogicR<"dmul", GPR64Opnd, 1, II_DMUL, mul>, ADD_FM<0x1c, 0x03>, ASE_CNMIPS { let Defs = [HI0, LO0, P0, P1, P2]; } let AdditionalPredicates = [NotInMicroMips] in { // Extract a signed bit field /+32 def EXTS : ExtsCins<"exts", II_EXT, GPR64Opnd, immZExt5>, EXTS_FM<0x3a>, ASE_MIPS64_CNMIPS; def EXTS32: ExtsCins<"exts32", II_EXT, GPR64Opnd, immZExt5Plus32>, EXTS_FM<0x3b>, ASE_MIPS64_CNMIPS; // Clear and insert a bit field /+32 def CINS : ExtsCins<"cins", II_INS, GPR64Opnd, immZExt5, MipsCIns>, EXTS_FM<0x32>, ASE_MIPS64_CNMIPS; def CINS32: ExtsCins<"cins32", II_INS, GPR64Opnd, immZExt5Plus32, MipsCIns>, EXTS_FM<0x33>, ASE_MIPS64_CNMIPS; let isCodeGenOnly = 1 in { def CINS_i32 : ExtsCins<"cins", II_INS, GPR32Opnd, immZExt5, MipsCIns>, EXTS_FM<0x32>, ASE_MIPS64_CNMIPS; def CINS64_32 :InstSE<(outs GPR64Opnd:$rt), (ins GPR32Opnd:$rs, uimm5:$pos, uimm5:$lenm1), "cins\t$rt, $rs, $pos, $lenm1", [], II_INS, FrmR, "cins">, EXTS_FM<0x32>, ASE_MIPS64_CNMIPS; } } // Move to multiplier/product register def MTM0 : MoveToLOHI<"mtm0", GPR64Opnd, [MPL0, P0, P1, P2]>, MTMR_FM<0x08>, ASE_CNMIPS; def MTM1 : MoveToLOHI<"mtm1", GPR64Opnd, [MPL1, P0, P1, P2]>, MTMR_FM<0x0c>, ASE_CNMIPS; def MTM2 : MoveToLOHI<"mtm2", GPR64Opnd, [MPL2, P0, P1, P2]>, MTMR_FM<0x0d>, ASE_CNMIPS; def MTP0 : MoveToLOHI<"mtp0", GPR64Opnd, [P0]>, MTMR_FM<0x09>, ASE_CNMIPS; def MTP1 : MoveToLOHI<"mtp1", GPR64Opnd, [P1]>, MTMR_FM<0x0a>, ASE_CNMIPS; def MTP2 : MoveToLOHI<"mtp2", GPR64Opnd, [P2]>, MTMR_FM<0x0b>, ASE_CNMIPS; // Count Ones in a Word/Doubleword def POP : Count1s<"pop", GPR32Opnd>, POP_FM<0x2c>, ASE_CNMIPS; def DPOP : Count1s<"dpop", GPR64Opnd>, POP_FM<0x2d>, ASE_CNMIPS; // Set on equal/not equal def SEQ : SetCC64_R<"seq", seteq>, SEQ_FM<0x2a>, ASE_CNMIPS; def SEQi : SetCC64_I<"seqi", seteq>, SEQI_FM<0x2e>, ASE_CNMIPS; def SNE : SetCC64_R<"sne", setne>, SEQ_FM<0x2b>, ASE_CNMIPS; def SNEi : SetCC64_I<"snei", setne>, SEQI_FM<0x2f>, ASE_CNMIPS; // 192-bit x 64-bit Unsigned Multiply and Add def V3MULU: ArithLogicR<"v3mulu", GPR64Opnd, 0, II_DMUL>, ADD_FM<0x1c, 0x11>, ASE_CNMIPS { let Defs = [P0, P1, P2]; } // 64-bit Unsigned Multiply and Add Move def VMM0 : ArithLogicR<"vmm0", GPR64Opnd, 0, II_DMUL>, ADD_FM<0x1c, 0x10>, ASE_CNMIPS { let Defs = [MPL0, P0, P1, P2]; } // 64-bit Unsigned Multiply and Add def VMULU : ArithLogicR<"vmulu", GPR64Opnd, 0, II_DMUL>, ADD_FM<0x1c, 0x0f>, ASE_CNMIPS { let Defs = [MPL1, MPL2, P0, P1, P2]; } // Move between CPU and coprocessor registers def DMFC2_OCTEON : MFC2OP<"dmfc2", GPR64Opnd, II_DMFC2>, MFC2OP_FM<0x12, 1>, ASE_CNMIPS; def DMTC2_OCTEON : MFC2OP<"dmtc2", GPR64Opnd, II_DMTC2>, MFC2OP_FM<0x12, 5>, ASE_CNMIPS; } } /// Move between CPU and coprocessor registers let DecoderNamespace = "Mips64", Predicates = [HasMips64] in { def DMFC0 : MFC3OP<"dmfc0", GPR64Opnd, COP0Opnd, II_DMFC0>, MFC3OP_FM<0x10, 1, 0>, ISA_MIPS3; def DMTC0 : MTC3OP<"dmtc0", COP0Opnd, GPR64Opnd, II_DMTC0>, MFC3OP_FM<0x10, 5, 0>, ISA_MIPS3; def DMFC2 : MFC3OP<"dmfc2", GPR64Opnd, COP2Opnd, II_DMFC2>, MFC3OP_FM<0x12, 1, 0>, ISA_MIPS3; def DMTC2 : MTC3OP<"dmtc2", COP2Opnd, GPR64Opnd, II_DMTC2>, MFC3OP_FM<0x12, 5, 0>, ISA_MIPS3; } /// Move between CPU and guest coprocessor registers (Virtualization ASE) let DecoderNamespace = "Mips64" in { def DMFGC0 : MFC3OP<"dmfgc0", GPR64Opnd, COP0Opnd, II_DMFGC0>, MFC3OP_FM<0x10, 3, 1>, ISA_MIPS64R5, ASE_VIRT; def DMTGC0 : MTC3OP<"dmtgc0", COP0Opnd, GPR64Opnd, II_DMTGC0>, MFC3OP_FM<0x10, 3, 3>, ISA_MIPS64R5, ASE_VIRT; } let AdditionalPredicates = [UseIndirectJumpsHazard] in def JALRHB64Pseudo : JumpLinkRegPseudo; //===----------------------------------------------------------------------===// // Arbitrary patterns that map to one or more instructions //===----------------------------------------------------------------------===// // Materialize i64 constants. defm : MaterializeImms; def : MipsPat<(i64 immZExt32Low16Zero:$imm), (DSLL (ORi64 ZERO_64, (HI16 imm:$imm)), 16)>; def : MipsPat<(i64 immZExt32:$imm), (ORi64 (DSLL (ORi64 ZERO_64, (HI16 imm:$imm)), 16), (LO16 imm:$imm))>; // extended loads def : MipsPat<(i64 (extloadi1 addr:$src)), (LB64 addr:$src)>; def : MipsPat<(i64 (extloadi8 addr:$src)), (LB64 addr:$src)>; def : MipsPat<(i64 (extloadi16 addr:$src)), (LH64 addr:$src)>; def : MipsPat<(i64 (extloadi32 addr:$src)), (LW64 addr:$src)>; // hi/lo relocs let AdditionalPredicates = [NotInMicroMips] in defm : MipsHiLoRelocs, SYM_32; def : MipsPat<(MipsGotHi tglobaladdr:$in), (LUi64 tglobaladdr:$in)>; def : MipsPat<(MipsGotHi texternalsym:$in), (LUi64 texternalsym:$in)>; // highest/higher/hi/lo relocs let AdditionalPredicates = [NotInMicroMips] in { def : MipsPat<(MipsJmpLink (i64 texternalsym:$dst)), (JAL texternalsym:$dst)>, SYM_64; def : MipsPat<(MipsHighest (i64 tglobaladdr:$in)), (LUi64 tglobaladdr:$in)>, SYM_64; def : MipsPat<(MipsHighest (i64 tblockaddress:$in)), (LUi64 tblockaddress:$in)>, SYM_64; def : MipsPat<(MipsHighest (i64 tjumptable:$in)), (LUi64 tjumptable:$in)>, SYM_64; def : MipsPat<(MipsHighest (i64 tconstpool:$in)), (LUi64 tconstpool:$in)>, SYM_64; def : MipsPat<(MipsHighest (i64 tglobaltlsaddr:$in)), (LUi64 tglobaltlsaddr:$in)>, SYM_64; def : MipsPat<(MipsHighest (i64 texternalsym:$in)), (LUi64 texternalsym:$in)>, SYM_64; def : MipsPat<(MipsHigher (i64 tglobaladdr:$in)), (DADDiu ZERO_64, tglobaladdr:$in)>, SYM_64; def : MipsPat<(MipsHigher (i64 tblockaddress:$in)), (DADDiu ZERO_64, tblockaddress:$in)>, SYM_64; def : MipsPat<(MipsHigher (i64 tjumptable:$in)), (DADDiu ZERO_64, tjumptable:$in)>, SYM_64; def : MipsPat<(MipsHigher (i64 tconstpool:$in)), (DADDiu ZERO_64, tconstpool:$in)>, SYM_64; def : MipsPat<(MipsHigher (i64 tglobaltlsaddr:$in)), (DADDiu ZERO_64, tglobaltlsaddr:$in)>, SYM_64; def : MipsPat<(MipsHigher (i64 texternalsym:$in)), (DADDiu ZERO_64, texternalsym:$in)>, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHigher (i64 tglobaladdr:$lo))), (DADDiu GPR64:$hi, tglobaladdr:$lo)>, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHigher (i64 tblockaddress:$lo))), (DADDiu GPR64:$hi, tblockaddress:$lo)>, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHigher (i64 tjumptable:$lo))), (DADDiu GPR64:$hi, tjumptable:$lo)>, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHigher (i64 tconstpool:$lo))), (DADDiu GPR64:$hi, tconstpool:$lo)>, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHigher (i64 tglobaltlsaddr:$lo))), (DADDiu GPR64:$hi, tglobaltlsaddr:$lo)>, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHi (i64 tglobaladdr:$lo))), (DADDiu GPR64:$hi, tglobaladdr:$lo)>, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHi (i64 tblockaddress:$lo))), (DADDiu GPR64:$hi, tblockaddress:$lo)>, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHi (i64 tjumptable:$lo))), (DADDiu GPR64:$hi, tjumptable:$lo)>, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHi (i64 tconstpool:$lo))), (DADDiu GPR64:$hi, tconstpool:$lo)>, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsHi (i64 tglobaltlsaddr:$lo))), (DADDiu GPR64:$hi, tglobaltlsaddr:$lo)>, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsLo (i64 tglobaladdr:$lo))), (DADDiu GPR64:$hi, tglobaladdr:$lo)>, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsLo (i64 tblockaddress:$lo))), (DADDiu GPR64:$hi, tblockaddress:$lo)>, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsLo (i64 tjumptable:$lo))), (DADDiu GPR64:$hi, tjumptable:$lo)>, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsLo (i64 tconstpool:$lo))), (DADDiu GPR64:$hi, tconstpool:$lo)>, SYM_64; def : MipsPat<(add GPR64:$hi, (MipsLo (i64 tglobaltlsaddr:$lo))), (DADDiu GPR64:$hi, tglobaltlsaddr:$lo)>, SYM_64; } // gp_rel relocs def : MipsPat<(add GPR64:$gp, (MipsGPRel tglobaladdr:$in)), (DADDiu GPR64:$gp, tglobaladdr:$in)>, ABI_N64; def : MipsPat<(add GPR64:$gp, (MipsGPRel tconstpool:$in)), (DADDiu GPR64:$gp, tconstpool:$in)>, ABI_N64; def : WrapperPat; def : WrapperPat; def : WrapperPat; def : WrapperPat; def : WrapperPat; def : WrapperPat; defm : BrcondPats; def : MipsPat<(brcond (i32 (setlt i64:$lhs, 1)), bb:$dst), (BLEZ64 i64:$lhs, bb:$dst)>; def : MipsPat<(brcond (i32 (setgt i64:$lhs, -1)), bb:$dst), (BGEZ64 i64:$lhs, bb:$dst)>; // setcc patterns let AdditionalPredicates = [NotInMicroMips] in { defm : SeteqPats; defm : SetlePats; defm : SetgtPats; defm : SetgePats; defm : SetgeImmPats; } // truncate def : MipsPat<(trunc (assertsext GPR64:$src)), (EXTRACT_SUBREG GPR64:$src, sub_32)>; // The forward compatibility strategy employed by MIPS requires us to treat // values as being sign extended to an infinite number of bits. This allows // existing software to run without modification on any future MIPS // implementation (e.g. 128-bit, or 1024-bit). Being compatible with this // strategy requires that truncation acts as a sign-extension for values being // fed into instructions operating on 32-bit values. Such instructions have // undefined results if this is not true. // For our case, this means that we can't issue an extract_subreg for nodes // such as (trunc:i32 (assertzext:i64 X, i32)), because the sign-bit of the // lower subreg would not be replicated into the upper half. def : MipsPat<(trunc (assertzext_lt_i32 GPR64:$src)), (EXTRACT_SUBREG GPR64:$src, sub_32)>; def : MipsPat<(i32 (trunc GPR64:$src)), (SLL (EXTRACT_SUBREG GPR64:$src, sub_32), 0)>; // variable shift instructions patterns def : MipsPat<(shl GPR64:$rt, (i32 (trunc GPR64:$rs))), (DSLLV GPR64:$rt, (EXTRACT_SUBREG GPR64:$rs, sub_32))>; def : MipsPat<(srl GPR64:$rt, (i32 (trunc GPR64:$rs))), (DSRLV GPR64:$rt, (EXTRACT_SUBREG GPR64:$rs, sub_32))>; def : MipsPat<(sra GPR64:$rt, (i32 (trunc GPR64:$rs))), (DSRAV GPR64:$rt, (EXTRACT_SUBREG GPR64:$rs, sub_32))>; let AdditionalPredicates = [NotInMicroMips] in { def : MipsPat<(rotr GPR64:$rt, (i32 (trunc GPR64:$rs))), (DROTRV GPR64:$rt, (EXTRACT_SUBREG GPR64:$rs, sub_32))>; } // 32-to-64-bit extension def : MipsPat<(i64 (anyext GPR32:$src)), (INSERT_SUBREG (i64 (IMPLICIT_DEF)), GPR32:$src, sub_32)>; def : MipsPat<(i64 (zext GPR32:$src)), (DSRL (DSLL64_32 GPR32:$src), 32)>; def : MipsPat<(i64 (sext GPR32:$src)), (SLL64_32 GPR32:$src)>; let AdditionalPredicates = [NotInMicroMips] in { def : MipsPat<(i64 (zext GPR32:$src)), (DEXT64_32 GPR32:$src, 0, 32)>, ISA_MIPS64R2; def : MipsPat<(i64 (zext (i32 (shl GPR32:$rt, immZExt5:$imm)))), (CINS64_32 GPR32:$rt, imm:$imm, (immZExt5To31 imm:$imm))>, ASE_MIPS64_CNMIPS; } // Sign extend in register def : MipsPat<(i64 (sext_inreg GPR64:$src, i32)), (SLL64_64 GPR64:$src)>; // bswap MipsPattern def : MipsPat<(bswap GPR64:$rt), (DSHD (DSBH GPR64:$rt))>, ISA_MIPS64R2; // Carry pattern let AdditionalPredicates = [NotInMicroMips] in { def : MipsPat<(subc GPR64:$lhs, GPR64:$rhs), (DSUBu GPR64:$lhs, GPR64:$rhs)>; def : MipsPat<(addc GPR64:$lhs, GPR64:$rhs), (DADDu GPR64:$lhs, GPR64:$rhs)>, ASE_NOT_DSP; def : MipsPat<(addc GPR64:$lhs, immSExt16:$imm), (DADDiu GPR64:$lhs, imm:$imm)>, ASE_NOT_DSP; } // Octeon bbit0/bbit1 MipsPattern def : MipsPat<(brcond (i32 (seteq (and i64:$lhs, PowerOf2LO:$mask), 0)), bb:$dst), (BBIT0 i64:$lhs, (Log2LO PowerOf2LO:$mask), bb:$dst)>, ASE_MIPS64_CNMIPS; def : MipsPat<(brcond (i32 (seteq (and i64:$lhs, PowerOf2HI:$mask), 0)), bb:$dst), (BBIT032 i64:$lhs, (Log2HI PowerOf2HI:$mask), bb:$dst)>, ASE_MIPS64_CNMIPS; def : MipsPat<(brcond (i32 (setne (and i64:$lhs, PowerOf2LO:$mask), 0)), bb:$dst), (BBIT1 i64:$lhs, (Log2LO PowerOf2LO:$mask), bb:$dst)>, ASE_MIPS64_CNMIPS; def : MipsPat<(brcond (i32 (setne (and i64:$lhs, PowerOf2HI:$mask), 0)), bb:$dst), (BBIT132 i64:$lhs, (Log2HI PowerOf2HI:$mask), bb:$dst)>, ASE_MIPS64_CNMIPS; def : MipsPat<(brcond (i32 (seteq (and i32:$lhs, PowerOf2LO_i32:$mask), 0)), bb:$dst), (BBIT0 (INSERT_SUBREG (i64 (IMPLICIT_DEF)), i32:$lhs, sub_32), (Log2LO PowerOf2LO_i32:$mask), bb:$dst)>, ASE_MIPS64_CNMIPS; def : MipsPat<(brcond (i32 (setne (and i32:$lhs, PowerOf2LO_i32:$mask), 0)), bb:$dst), (BBIT1 (INSERT_SUBREG (i64 (IMPLICIT_DEF)), i32:$lhs, sub_32), (Log2LO PowerOf2LO_i32:$mask), bb:$dst)>, ASE_MIPS64_CNMIPS; // Atomic load patterns. def : MipsPat<(atomic_load_8 addr:$a), (LB64 addr:$a)>; def : MipsPat<(atomic_load_16 addr:$a), (LH64 addr:$a)>; def : MipsPat<(atomic_load_32 addr:$a), (LW64 addr:$a)>; def : MipsPat<(atomic_load_64 addr:$a), (LD addr:$a)>; // Atomic store patterns. def : MipsPat<(atomic_store_8 addr:$a, GPR64:$v), (SB64 GPR64:$v, addr:$a)>; def : MipsPat<(atomic_store_16 addr:$a, GPR64:$v), (SH64 GPR64:$v, addr:$a)>; def : MipsPat<(atomic_store_32 addr:$a, GPR64:$v), (SW64 GPR64:$v, addr:$a)>; def : MipsPat<(atomic_store_64 addr:$a, GPR64:$v), (SD GPR64:$v, addr:$a)>; //===----------------------------------------------------------------------===// // Instruction aliases //===----------------------------------------------------------------------===// let AdditionalPredicates = [NotInMicroMips] in { def : MipsInstAlias<"move $dst, $src", (OR64 GPR64Opnd:$dst, GPR64Opnd:$src, ZERO_64), 1>, GPR_64; def : MipsInstAlias<"move $dst, $src", (DADDu GPR64Opnd:$dst, GPR64Opnd:$src, ZERO_64), 1>, GPR_64; def : MipsInstAlias<"dadd $rs, $rt, $imm", (DADDi GPR64Opnd:$rs, GPR64Opnd:$rt, simm16_64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"dadd $rs, $imm", (DADDi GPR64Opnd:$rs, GPR64Opnd:$rs, simm16_64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"daddu $rs, $rt, $imm", (DADDiu GPR64Opnd:$rs, GPR64Opnd:$rt, simm16_64:$imm), 0>, ISA_MIPS3; def : MipsInstAlias<"daddu $rs, $imm", (DADDiu GPR64Opnd:$rs, GPR64Opnd:$rs, simm16_64:$imm), 0>, ISA_MIPS3; defm : OneOrTwoOperandMacroImmediateAlias<"and", ANDi64, GPR64Opnd, imm64>, ISA_MIPS3, GPR_64; defm : OneOrTwoOperandMacroImmediateAlias<"or", ORi64, GPR64Opnd, imm64>, ISA_MIPS3, GPR_64; defm : OneOrTwoOperandMacroImmediateAlias<"xor", XORi64, GPR64Opnd, imm64>, ISA_MIPS3, GPR_64; } let AdditionalPredicates = [NotInMicroMips] in { def : MipsInstAlias<"dneg $rt, $rs", (DSUB GPR64Opnd:$rt, ZERO_64, GPR64Opnd:$rs), 1>, ISA_MIPS3; def : MipsInstAlias<"dneg $rt", (DSUB GPR64Opnd:$rt, ZERO_64, GPR64Opnd:$rt), 1>, ISA_MIPS3; def : MipsInstAlias<"dnegu $rt, $rs", (DSUBu GPR64Opnd:$rt, ZERO_64, GPR64Opnd:$rs), 1>, ISA_MIPS3; def : MipsInstAlias<"dnegu $rt", (DSUBu GPR64Opnd:$rt, ZERO_64, GPR64Opnd:$rt), 1>, ISA_MIPS3; } def : MipsInstAlias<"dsubi $rs, $rt, $imm", (DADDi GPR64Opnd:$rs, GPR64Opnd:$rt, InvertedImOperand64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"dsubi $rs, $imm", (DADDi GPR64Opnd:$rs, GPR64Opnd:$rs, InvertedImOperand64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"dsub $rs, $rt, $imm", (DADDi GPR64Opnd:$rs, GPR64Opnd:$rt, InvertedImOperand64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"dsub $rs, $imm", (DADDi GPR64Opnd:$rs, GPR64Opnd:$rs, InvertedImOperand64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; let AdditionalPredicates = [NotInMicroMips] in { def : MipsInstAlias<"dsubu $rt, $rs, $imm", (DADDiu GPR64Opnd:$rt, GPR64Opnd:$rs, InvertedImOperand64:$imm), 0>, ISA_MIPS3; def : MipsInstAlias<"dsubu $rs, $imm", (DADDiu GPR64Opnd:$rs, GPR64Opnd:$rs, InvertedImOperand64:$imm), 0>, ISA_MIPS3; } def : MipsInstAlias<"dsra $rd, $rt, $rs", (DSRAV GPR64Opnd:$rd, GPR64Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS3; let AdditionalPredicates = [NotInMicroMips] in { def : MipsInstAlias<"dsll $rd, $rt, $rs", (DSLLV GPR64Opnd:$rd, GPR64Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS3; def : MipsInstAlias<"dsrl $rd, $rt, $rs", (DSRLV GPR64Opnd:$rd, GPR64Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS3; def : MipsInstAlias<"dsrl $rd, $rt", (DSRLV GPR64Opnd:$rd, GPR64Opnd:$rd, GPR32Opnd:$rt), 0>, ISA_MIPS3; def : MipsInstAlias<"dsll $rd, $rt", (DSLLV GPR64Opnd:$rd, GPR64Opnd:$rd, GPR32Opnd:$rt), 0>, ISA_MIPS3; def : MipsInstAlias<"dins $rt, $rs, $pos, $size", (DINSM GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5:$pos, uimm_range_2_64:$size), 0>, ISA_MIPS64R2; def : MipsInstAlias<"dins $rt, $rs, $pos, $size", (DINSU GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5_plus32:$pos, uimm5_plus1:$size), 0>, ISA_MIPS64R2; def : MipsInstAlias<"dext $rt, $rs, $pos, $size", (DEXTM GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5:$pos, uimm5_plus33:$size), 0>, ISA_MIPS64R2; def : MipsInstAlias<"dext $rt, $rs, $pos, $size", (DEXTU GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5_plus32:$pos, uimm5_plus1:$size), 0>, ISA_MIPS64R2; def : MipsInstAlias<"jalr.hb $rs", (JALR_HB64 RA_64, GPR64Opnd:$rs), 1>, ISA_MIPS64; // Two operand (implicit 0 selector) versions: def : MipsInstAlias<"dmtc0 $rt, $rd", (DMTC0 COP0Opnd:$rd, GPR64Opnd:$rt, 0), 0>; def : MipsInstAlias<"dmfc0 $rt, $rd", (DMFC0 GPR64Opnd:$rt, COP0Opnd:$rd, 0), 0>; def : MipsInstAlias<"dmfgc0 $rt, $rd", (DMFGC0 GPR64Opnd:$rt, COP0Opnd:$rd, 0), 0>, ISA_MIPS64R5, ASE_VIRT; def : MipsInstAlias<"dmtgc0 $rt, $rd", (DMTGC0 COP0Opnd:$rd, GPR64Opnd:$rt, 0), 0>, ISA_MIPS64R5, ASE_VIRT; } def : MipsInstAlias<"dmfc2 $rt, $rd", (DMFC2 GPR64Opnd:$rt, COP2Opnd:$rd, 0), 0>; def : MipsInstAlias<"dmtc2 $rt, $rd", (DMTC2 COP2Opnd:$rd, GPR64Opnd:$rt, 0), 0>; def : MipsInstAlias<"synciobdma", (SYNC 0x2), 0>, ASE_MIPS64_CNMIPS; def : MipsInstAlias<"syncs", (SYNC 0x6), 0>, ASE_MIPS64_CNMIPS; def : MipsInstAlias<"syncw", (SYNC 0x4), 0>, ASE_MIPS64_CNMIPS; def : MipsInstAlias<"syncws", (SYNC 0x5), 0>, ASE_MIPS64_CNMIPS; // cnMIPS Aliases. // bbit* with $p 32-63 converted to bbit*32 with $p 0-31 def : MipsInstAlias<"bbit0 $rs, $p, $offset", (BBIT032 GPR64Opnd:$rs, uimm5_plus32_normalize_64:$p, brtarget:$offset), 0>, ASE_CNMIPS; def : MipsInstAlias<"bbit1 $rs, $p, $offset", (BBIT132 GPR64Opnd:$rs, uimm5_plus32_normalize_64:$p, brtarget:$offset), 0>, ASE_CNMIPS; // exts with $pos 32-63 in converted to exts32 with $pos 0-31 def : MipsInstAlias<"exts $rt, $rs, $pos, $lenm1", (EXTS32 GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5_plus32_normalize:$pos, uimm5:$lenm1), 0>, ASE_MIPS64_CNMIPS; def : MipsInstAlias<"exts $rt, $pos, $lenm1", (EXTS32 GPR64Opnd:$rt, GPR64Opnd:$rt, uimm5_plus32_normalize:$pos, uimm5:$lenm1), 0>, ASE_MIPS64_CNMIPS; // cins with $pos 32-63 in converted to cins32 with $pos 0-31 def : MipsInstAlias<"cins $rt, $rs, $pos, $lenm1", (CINS32 GPR64Opnd:$rt, GPR64Opnd:$rs, uimm5_plus32_normalize:$pos, uimm5:$lenm1), 0>, ASE_MIPS64_CNMIPS; def : MipsInstAlias<"cins $rt, $pos, $lenm1", (CINS32 GPR64Opnd:$rt, GPR64Opnd:$rt, uimm5_plus32_normalize:$pos, uimm5:$lenm1), 0>, ASE_MIPS64_CNMIPS; //===----------------------------------------------------------------------===// // Assembler Pseudo Instructions //===----------------------------------------------------------------------===// class LoadImmediate64 : MipsAsmPseudoInst<(outs RO:$rt), (ins Od:$imm64), !strconcat(instr_asm, "\t$rt, $imm64")> ; def LoadImm64 : LoadImmediate64<"dli", imm64, GPR64Opnd>; def LoadAddrReg64 : MipsAsmPseudoInst<(outs GPR64Opnd:$rt), (ins mem:$addr), "dla\t$rt, $addr">; def LoadAddrImm64 : MipsAsmPseudoInst<(outs GPR64Opnd:$rt), (ins imm64:$imm64), "dla\t$rt, $imm64">; def DMULImmMacro : MipsAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt, simm32_relaxed:$imm), "dmul\t$rs, $rt, $imm">, ISA_MIPS3_NOT_32R6_64R6; def DMULOMacro : MipsAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt, GPR64Opnd:$rd), "dmulo\t$rs, $rt, $rd">, ISA_MIPS3_NOT_32R6_64R6; def DMULOUMacro : MipsAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt, GPR64Opnd:$rd), "dmulou\t$rs, $rt, $rd">, ISA_MIPS3_NOT_32R6_64R6; def DMULMacro : MipsAsmPseudoInst<(outs), (ins GPR64Opnd:$rs, GPR64Opnd:$rt, GPR64Opnd:$rd), "dmul\t$rs, $rt, $rd"> { let InsnPredicates = [HasMips3, NotMips64r6, NotCnMips]; } let AdditionalPredicates = [NotInMicroMips] in { def DSDivMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, GPR64Opnd:$rt), "ddiv\t$rd, $rs, $rt">, ISA_MIPS3_NOT_32R6_64R6; def DSDivIMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, imm64:$imm), "ddiv\t$rd, $rs, $imm">, ISA_MIPS3_NOT_32R6_64R6; def DUDivMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, GPR64Opnd:$rt), "ddivu\t$rd, $rs, $rt">, ISA_MIPS3_NOT_32R6_64R6; def DUDivIMacro : MipsAsmPseudoInst<(outs GPR64Opnd:$rd), (ins GPR64Opnd:$rs, imm64:$imm), "ddivu\t$rd, $rs, $imm">, ISA_MIPS3_NOT_32R6_64R6; // GAS expands 'div' and 'ddiv' differently when the destination // register is $zero and the instruction is in the two operand // form. 'ddiv' gets expanded, while 'div' is not expanded. def : MipsInstAlias<"ddiv $rs, $rt", (DSDivMacro GPR64Opnd:$rs, GPR64Opnd:$rs, GPR64Opnd:$rt), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"ddiv $rd, $imm", (DSDivIMacro GPR64Opnd:$rd, GPR64Opnd:$rd, imm64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; // GAS expands 'divu' and 'ddivu' differently when the destination // register is $zero and the instruction is in the two operand // form. 'ddivu' gets expanded, while 'divu' is not expanded. def : MipsInstAlias<"ddivu $rt, $rs", (DUDivMacro GPR64Opnd:$rt, GPR64Opnd:$rt, GPR64Opnd:$rs), 0>, ISA_MIPS3_NOT_32R6_64R6; def : MipsInstAlias<"ddivu $rd, $imm", (DUDivIMacro GPR64Opnd:$rd, GPR64Opnd:$rd, imm64:$imm), 0>, ISA_MIPS3_NOT_32R6_64R6; } def NORImm64 : NORIMM_DESC_BASE, GPR_64; def : MipsInstAlias<"nor\t$rs, $imm", (NORImm64 GPR64Opnd:$rs, GPR64Opnd:$rs, imm64:$imm)>, GPR_64; def SLTImm64 : MipsAsmPseudoInst<(outs GPR64Opnd:$rs), (ins GPR64Opnd:$rt, imm64:$imm), "slt\t$rs, $rt, $imm">, GPR_64; def : MipsInstAlias<"slt\t$rs, $imm", (SLTImm64 GPR64Opnd:$rs, GPR64Opnd:$rs, imm64:$imm)>, GPR_64; def SLTUImm64 : MipsAsmPseudoInst<(outs GPR64Opnd:$rs), (ins GPR64Opnd:$rt, imm64:$imm), "sltu\t$rs, $rt, $imm">, GPR_64; def : MipsInstAlias<"sltu\t$rs, $imm", (SLTUImm64 GPR64Opnd:$rs, GPR64Opnd:$rs, imm64:$imm)>, GPR_64; Index: llvm/trunk/lib/Target/Mips/MipsInstrInfo.td =================================================================== --- llvm/trunk/lib/Target/Mips/MipsInstrInfo.td (revision 336327) +++ llvm/trunk/lib/Target/Mips/MipsInstrInfo.td (revision 336328) @@ -1,3220 +1,3274 @@ //===- MipsInstrInfo.td - Target Description for Mips Target -*- tablegen -*-=// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file contains the Mips implementation of the TargetInstrInfo class. // //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // Mips profiles and nodes //===----------------------------------------------------------------------===// def SDT_MipsJmpLink : SDTypeProfile<0, 1, [SDTCisVT<0, iPTR>]>; def SDT_MipsCMov : SDTypeProfile<1, 4, [SDTCisSameAs<0, 1>, SDTCisSameAs<1, 2>, SDTCisSameAs<3, 4>, SDTCisInt<4>]>; def SDT_MipsCallSeqStart : SDCallSeqStart<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; def SDT_MipsCallSeqEnd : SDCallSeqEnd<[SDTCisVT<0, i32>, SDTCisVT<1, i32>]>; def SDT_MFLOHI : SDTypeProfile<1, 1, [SDTCisInt<0>, SDTCisVT<1, untyped>]>; def SDT_MTLOHI : SDTypeProfile<1, 2, [SDTCisVT<0, untyped>, SDTCisInt<1>, SDTCisSameAs<1, 2>]>; def SDT_MipsMultDiv : SDTypeProfile<1, 2, [SDTCisVT<0, untyped>, SDTCisInt<1>, SDTCisSameAs<1, 2>]>; def SDT_MipsMAddMSub : SDTypeProfile<1, 3, [SDTCisVT<0, untyped>, SDTCisSameAs<0, 3>, SDTCisVT<1, i32>, SDTCisSameAs<1, 2>]>; def SDT_MipsDivRem16 : SDTypeProfile<0, 2, [SDTCisInt<0>, SDTCisSameAs<0, 1>]>; def SDT_MipsThreadPointer : SDTypeProfile<1, 0, [SDTCisPtrTy<0>]>; def SDT_Sync : SDTypeProfile<0, 1, [SDTCisVT<0, i32>]>; def SDT_Ext : SDTypeProfile<1, 3, [SDTCisInt<0>, SDTCisSameAs<0, 1>, SDTCisVT<2, i32>, SDTCisSameAs<2, 3>]>; def SDT_Ins : SDTypeProfile<1, 4, [SDTCisInt<0>, SDTCisSameAs<0, 1>, SDTCisVT<2, i32>, SDTCisSameAs<2, 3>, SDTCisSameAs<0, 4>]>; def SDTMipsLoadLR : SDTypeProfile<1, 2, [SDTCisInt<0>, SDTCisPtrTy<1>, SDTCisSameAs<0, 2>]>; // Call def MipsJmpLink : SDNode<"MipsISD::JmpLink",SDT_MipsJmpLink, [SDNPHasChain, SDNPOutGlue, SDNPOptInGlue, SDNPVariadic]>; // Tail call def MipsTailCall : SDNode<"MipsISD::TailCall", SDT_MipsJmpLink, [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; // Hi and Lo nodes are used to handle global addresses. Used on // MipsISelLowering to lower stuff like GlobalAddress, ExternalSymbol // static model. (nothing to do with Mips Registers Hi and Lo) // Hi is the odd node out, on MIPS64 it can expand to either daddiu when // using static relocations with 64 bit symbols, or lui when using 32 bit // symbols. def MipsHigher : SDNode<"MipsISD::Higher", SDTIntUnaryOp>; def MipsHighest : SDNode<"MipsISD::Highest", SDTIntUnaryOp>; def MipsHi : SDNode<"MipsISD::Hi", SDTIntUnaryOp>; def MipsLo : SDNode<"MipsISD::Lo", SDTIntUnaryOp>; def MipsGPRel : SDNode<"MipsISD::GPRel", SDTIntUnaryOp>; // Hi node for accessing the GOT. def MipsGotHi : SDNode<"MipsISD::GotHi", SDTIntUnaryOp>; // TlsGd node is used to handle General Dynamic TLS def MipsTlsGd : SDNode<"MipsISD::TlsGd", SDTIntUnaryOp>; // TprelHi and TprelLo nodes are used to handle Local Exec TLS def MipsTprelHi : SDNode<"MipsISD::TprelHi", SDTIntUnaryOp>; def MipsTprelLo : SDNode<"MipsISD::TprelLo", SDTIntUnaryOp>; // Thread pointer def MipsThreadPointer: SDNode<"MipsISD::ThreadPointer", SDT_MipsThreadPointer>; // Return def MipsRet : SDNode<"MipsISD::Ret", SDTNone, [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; def MipsERet : SDNode<"MipsISD::ERet", SDTNone, [SDNPHasChain, SDNPOptInGlue, SDNPSideEffect]>; // These are target-independent nodes, but have target-specific formats. def callseq_start : SDNode<"ISD::CALLSEQ_START", SDT_MipsCallSeqStart, [SDNPHasChain, SDNPSideEffect, SDNPOutGlue]>; def callseq_end : SDNode<"ISD::CALLSEQ_END", SDT_MipsCallSeqEnd, [SDNPHasChain, SDNPSideEffect, SDNPOptInGlue, SDNPOutGlue]>; // Nodes used to extract LO/HI registers. def MipsMFHI : SDNode<"MipsISD::MFHI", SDT_MFLOHI>; def MipsMFLO : SDNode<"MipsISD::MFLO", SDT_MFLOHI>; // Node used to insert 32-bit integers to LOHI register pair. def MipsMTLOHI : SDNode<"MipsISD::MTLOHI", SDT_MTLOHI>; // Mult nodes. def MipsMult : SDNode<"MipsISD::Mult", SDT_MipsMultDiv>; def MipsMultu : SDNode<"MipsISD::Multu", SDT_MipsMultDiv>; // MAdd*/MSub* nodes def MipsMAdd : SDNode<"MipsISD::MAdd", SDT_MipsMAddMSub>; def MipsMAddu : SDNode<"MipsISD::MAddu", SDT_MipsMAddMSub>; def MipsMSub : SDNode<"MipsISD::MSub", SDT_MipsMAddMSub>; def MipsMSubu : SDNode<"MipsISD::MSubu", SDT_MipsMAddMSub>; // DivRem(u) nodes def MipsDivRem : SDNode<"MipsISD::DivRem", SDT_MipsMultDiv>; def MipsDivRemU : SDNode<"MipsISD::DivRemU", SDT_MipsMultDiv>; def MipsDivRem16 : SDNode<"MipsISD::DivRem16", SDT_MipsDivRem16, [SDNPOutGlue]>; def MipsDivRemU16 : SDNode<"MipsISD::DivRemU16", SDT_MipsDivRem16, [SDNPOutGlue]>; // Target constant nodes that are not part of any isel patterns and remain // unchanged can cause instructions with illegal operands to be emitted. // Wrapper node patterns give the instruction selector a chance to replace // target constant nodes that would otherwise remain unchanged with ADDiu // nodes. Without these wrapper node patterns, the following conditional move // instruction is emitted when function cmov2 in test/CodeGen/Mips/cmov.ll is // compiled: // movn %got(d)($gp), %got(c)($gp), $4 // This instruction is illegal since movn can take only register operands. def MipsWrapper : SDNode<"MipsISD::Wrapper", SDTIntBinOp>; def MipsSync : SDNode<"MipsISD::Sync", SDT_Sync, [SDNPHasChain,SDNPSideEffect]>; def MipsExt : SDNode<"MipsISD::Ext", SDT_Ext>; def MipsIns : SDNode<"MipsISD::Ins", SDT_Ins>; def MipsCIns : SDNode<"MipsISD::CIns", SDT_Ext>; def MipsLWL : SDNode<"MipsISD::LWL", SDTMipsLoadLR, [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; def MipsLWR : SDNode<"MipsISD::LWR", SDTMipsLoadLR, [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; def MipsSWL : SDNode<"MipsISD::SWL", SDTStore, [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; def MipsSWR : SDNode<"MipsISD::SWR", SDTStore, [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; def MipsLDL : SDNode<"MipsISD::LDL", SDTMipsLoadLR, [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; def MipsLDR : SDNode<"MipsISD::LDR", SDTMipsLoadLR, [SDNPHasChain, SDNPMayLoad, SDNPMemOperand]>; def MipsSDL : SDNode<"MipsISD::SDL", SDTStore, [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; def MipsSDR : SDNode<"MipsISD::SDR", SDTStore, [SDNPHasChain, SDNPMayStore, SDNPMemOperand]>; //===----------------------------------------------------------------------===// // Mips Instruction Predicate Definitions. //===----------------------------------------------------------------------===// def HasMips2 : Predicate<"Subtarget->hasMips2()">, AssemblerPredicate<"FeatureMips2">; def HasMips3_32 : Predicate<"Subtarget->hasMips3_32()">, AssemblerPredicate<"FeatureMips3_32">; def HasMips3_32r2 : Predicate<"Subtarget->hasMips3_32r2()">, AssemblerPredicate<"FeatureMips3_32r2">; def HasMips3 : Predicate<"Subtarget->hasMips3()">, AssemblerPredicate<"FeatureMips3">; def NotMips3 : Predicate<"!Subtarget->hasMips3()">, AssemblerPredicate<"!FeatureMips3">; def HasMips4_32 : Predicate<"Subtarget->hasMips4_32()">, AssemblerPredicate<"FeatureMips4_32">; def NotMips4_32 : Predicate<"!Subtarget->hasMips4_32()">, AssemblerPredicate<"!FeatureMips4_32">; def HasMips4_32r2 : Predicate<"Subtarget->hasMips4_32r2()">, AssemblerPredicate<"FeatureMips4_32r2">; def HasMips5_32r2 : Predicate<"Subtarget->hasMips5_32r2()">, AssemblerPredicate<"FeatureMips5_32r2">; def HasMips32 : Predicate<"Subtarget->hasMips32()">, AssemblerPredicate<"FeatureMips32">; def HasMips32r2 : Predicate<"Subtarget->hasMips32r2()">, AssemblerPredicate<"FeatureMips32r2">; def HasMips32r5 : Predicate<"Subtarget->hasMips32r5()">, AssemblerPredicate<"FeatureMips32r5">; def HasMips32r6 : Predicate<"Subtarget->hasMips32r6()">, AssemblerPredicate<"FeatureMips32r6">; def NotMips32r6 : Predicate<"!Subtarget->hasMips32r6()">, AssemblerPredicate<"!FeatureMips32r6">; def IsGP64bit : Predicate<"Subtarget->isGP64bit()">, AssemblerPredicate<"FeatureGP64Bit">; def IsGP32bit : Predicate<"!Subtarget->isGP64bit()">, AssemblerPredicate<"!FeatureGP64Bit">; def IsPTR64bit : Predicate<"Subtarget->isABI_N64()">, AssemblerPredicate<"FeaturePTR64Bit">; def IsPTR32bit : Predicate<"!Subtarget->isABI_N64()">, AssemblerPredicate<"!FeaturePTR64Bit">; def HasMips64 : Predicate<"Subtarget->hasMips64()">, AssemblerPredicate<"FeatureMips64">; def NotMips64 : Predicate<"!Subtarget->hasMips64()">, AssemblerPredicate<"!FeatureMips64">; def HasMips64r2 : Predicate<"Subtarget->hasMips64r2()">, AssemblerPredicate<"FeatureMips64r2">; def HasMips64r5 : Predicate<"Subtarget->hasMips64r5()">, AssemblerPredicate<"FeatureMips64r5">; def HasMips64r6 : Predicate<"Subtarget->hasMips64r6()">, AssemblerPredicate<"FeatureMips64r6">; def NotMips64r6 : Predicate<"!Subtarget->hasMips64r6()">, AssemblerPredicate<"!FeatureMips64r6">; def InMips16Mode : Predicate<"Subtarget->inMips16Mode()">, AssemblerPredicate<"FeatureMips16">; def NotInMips16Mode : Predicate<"!Subtarget->inMips16Mode()">, AssemblerPredicate<"!FeatureMips16">; def HasCnMips : Predicate<"Subtarget->hasCnMips()">, AssemblerPredicate<"FeatureCnMips">; def NotCnMips : Predicate<"!Subtarget->hasCnMips()">, AssemblerPredicate<"!FeatureCnMips">; def IsSym32 : Predicate<"Subtarget->HasSym32()">, AssemblerPredicate<"FeatureSym32">; def IsSym64 : Predicate<"!Subtarget->HasSym32()">, AssemblerPredicate<"!FeatureSym32">; def IsN64 : Predicate<"Subtarget->isABI_N64()">; def IsNotN64 : Predicate<"!Subtarget->isABI_N64()">; def RelocNotPIC : Predicate<"!TM.isPositionIndependent()">; def RelocPIC : Predicate<"TM.isPositionIndependent()">; def NoNaNsFPMath : Predicate<"TM.Options.NoNaNsFPMath">; def HasStdEnc : Predicate<"Subtarget->hasStandardEncoding()">, AssemblerPredicate<"!FeatureMips16">; def NotDSP : Predicate<"!Subtarget->hasDSP()">; def InMicroMips : Predicate<"Subtarget->inMicroMipsMode()">, AssemblerPredicate<"FeatureMicroMips">; def NotInMicroMips : Predicate<"!Subtarget->inMicroMipsMode()">, AssemblerPredicate<"!FeatureMicroMips">; def IsLE : Predicate<"Subtarget->isLittle()">; def IsBE : Predicate<"!Subtarget->isLittle()">; def IsNotNaCl : Predicate<"!Subtarget->isTargetNaCl()">; def UseTCCInDIV : AssemblerPredicate<"FeatureUseTCCInDIV">; def HasEVA : Predicate<"Subtarget->hasEVA()">, AssemblerPredicate<"FeatureEVA">; def HasMSA : Predicate<"Subtarget->hasMSA()">, AssemblerPredicate<"FeatureMSA">; def HasMadd4 : Predicate<"!Subtarget->disableMadd4()">, AssemblerPredicate<"!FeatureMadd4">; def HasMT : Predicate<"Subtarget->hasMT()">, AssemblerPredicate<"FeatureMT">; def UseIndirectJumpsHazard : Predicate<"Subtarget->useIndirectJumpsHazard()">, AssemblerPredicate<"FeatureUseIndirectJumpsHazard">; def NoIndirectJumpGuards : Predicate<"!Subtarget->useIndirectJumpsHazard()">, AssemblerPredicate<"!FeatureUseIndirectJumpsHazard">; def HasCRC : Predicate<"Subtarget->hasCRC()">, AssemblerPredicate<"FeatureCRC">; def HasVirt : Predicate<"Subtarget->hasVirt()">, AssemblerPredicate<"FeatureVirt">; def HasGINV : Predicate<"Subtarget->hasGINV()">, AssemblerPredicate<"FeatureGINV">; // TODO: Add support for FPOpFusion::Standard def AllowFPOpFusion : Predicate<"TM.Options.AllowFPOpFusion ==" " FPOpFusion::Fast">; //===----------------------------------------------------------------------===// // Mips GPR size adjectives. // They are mutually exclusive. //===----------------------------------------------------------------------===// class GPR_32 { list GPRPredicates = [IsGP32bit]; } class GPR_64 { list GPRPredicates = [IsGP64bit]; } class PTR_32 { list PTRPredicates = [IsPTR32bit]; } class PTR_64 { list PTRPredicates = [IsPTR64bit]; } //===----------------------------------------------------------------------===// // Mips Symbol size adjectives. // They are mutally exculsive. //===----------------------------------------------------------------------===// class SYM_32 { list SYMPredicates = [IsSym32]; } class SYM_64 { list SYMPredicates = [IsSym64]; } //===----------------------------------------------------------------------===// // Mips ISA/ASE membership and instruction group membership adjectives. // They are mutually exclusive. //===----------------------------------------------------------------------===// // FIXME: I'd prefer to use additive predicates to build the instruction sets // but we are short on assembler feature bits at the moment. Using a // subtractive predicate will hopefully keep us under the 32 predicate // limit long enough to develop an alternative way to handle P1||P2 // predicates. class ISA_MIPS1 { list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS1_NOT_MIPS3 { list InsnPredicates = [NotMips3]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS1_NOT_4_32 { list InsnPredicates = [NotMips4_32]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS1_NOT_32R6_64R6 { list InsnPredicates = [NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS2 { list InsnPredicates = [HasMips2]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS2_NOT_32R6_64R6 { list InsnPredicates = [HasMips2, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS3 { list InsnPredicates = [HasMips3]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS3_NOT_32R6_64R6 { list InsnPredicates = [HasMips3, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS32 { list InsnPredicates = [HasMips32]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS32_NOT_32R6_64R6 { list InsnPredicates = [HasMips32, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS32R2 { list InsnPredicates = [HasMips32r2]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS32R2_NOT_32R6_64R6 { list InsnPredicates = [HasMips32r2, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS32R5 { list InsnPredicates = [HasMips32r5]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS64 { list InsnPredicates = [HasMips64]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS64_NOT_64R6 { list InsnPredicates = [HasMips64, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS64R2 { list InsnPredicates = [HasMips64r2]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS64R5 { list InsnPredicates = [HasMips64r5]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS32R6 { list InsnPredicates = [HasMips32r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MIPS64R6 { list InsnPredicates = [HasMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ISA_MICROMIPS { list EncodingPredicates = [InMicroMips]; } class ISA_MICROMIPS32R5 { list InsnPredicates = [HasMips32r5]; list EncodingPredicates = [InMicroMips]; } class ISA_MICROMIPS32R6 { list InsnPredicates = [HasMips32r6]; list EncodingPredicates = [InMicroMips]; } class ISA_MICROMIPS64R6 { list InsnPredicates = [HasMips64r6]; list EncodingPredicates = [InMicroMips]; } class ISA_MICROMIPS32_NOT_MIPS32R6 { list InsnPredicates = [NotMips32r6]; list EncodingPredicates = [InMicroMips]; } class ASE_EVA { list ASEPredicate = [HasEVA]; } // The portions of MIPS-III that were also added to MIPS32 class INSN_MIPS3_32 { list InsnPredicates = [HasMips3_32]; list EncodingPredicates = [HasStdEnc]; } // The portions of MIPS-III that were also added to MIPS32 but were removed in // MIPS32r6 and MIPS64r6. class INSN_MIPS3_32_NOT_32R6_64R6 { list InsnPredicates = [HasMips3_32, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } // The portions of MIPS-III that were also added to MIPS32 class INSN_MIPS3_32R2 { list InsnPredicates = [HasMips3_32r2]; list EncodingPredicates = [HasStdEnc]; } // The portions of MIPS-IV that were also added to MIPS32. class INSN_MIPS4_32 { list InsnPredicates = [HasMips4_32]; list EncodingPredicates = [HasStdEnc]; } // The portions of MIPS-IV that were also added to MIPS32 but were removed in // MIPS32r6 and MIPS64r6. class INSN_MIPS4_32_NOT_32R6_64R6 { list InsnPredicates = [HasMips4_32, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } // The portions of MIPS-IV that were also added to MIPS32r2 but were removed in // MIPS32r6 and MIPS64r6. class INSN_MIPS4_32R2_NOT_32R6_64R6 { list InsnPredicates = [HasMips4_32r2, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } // The portions of MIPS-IV that were also added to MIPS32r2. class INSN_MIPS4_32R2 { list InsnPredicates = [HasMips4_32r2]; list EncodingPredicates = [HasStdEnc]; } // The portions of MIPS-V that were also added to MIPS32r2 but were removed in // MIPS32r6 and MIPS64r6. class INSN_MIPS5_32R2_NOT_32R6_64R6 { list InsnPredicates = [HasMips5_32r2, NotMips32r6, NotMips64r6]; list EncodingPredicates = [HasStdEnc]; } class ASE_CNMIPS { list ASEPredicate = [HasCnMips]; } class NOT_ASE_CNMIPS { list ASEPredicate = [NotCnMips]; } class ASE_MIPS64_CNMIPS { list ASEPredicate = [HasMips64, HasCnMips]; } class ASE_MSA { list ASEPredicate = [HasMSA]; } class ASE_MSA_NOT_MSA64 { list ASEPredicate = [HasMSA, NotMips64]; } class ASE_MSA64 { list ASEPredicate = [HasMSA, HasMips64]; } class ASE_MT { list ASEPredicate = [HasMT]; } class ASE_CRC { list ASEPredicate = [HasCRC]; } class ASE_VIRT { list ASEPredicate = [HasVirt]; } class ASE_GINV { list ASEPredicate = [HasGINV]; } // Class used for separating microMIPSr6 and microMIPS (r3) instruction. // It can be used only on instructions that doesn't inherit PredicateControl. class ISA_MICROMIPS_NOT_32R6 : PredicateControl { let InsnPredicates = [NotMips32r6]; let EncodingPredicates = [InMicroMips]; } class ASE_NOT_DSP { list ASEPredicate = [NotDSP]; } class MADD4 { list AdditionalPredicates = [HasMadd4]; } // Classses used for separating expansions that differ based on the ABI in // use. class ABI_N64 { list AdditionalPredicates = [IsN64]; } class ABI_NOT_N64 { list AdditionalPredicates = [IsNotN64]; } class FPOP_FUSION_FAST { list AdditionalPredicates = [AllowFPOpFusion]; } //===----------------------------------------------------------------------===// class MipsPat : Pat, PredicateControl { let EncodingPredicates = [HasStdEnc]; } class MipsInstAlias : InstAlias, PredicateControl; class IsCommutable { bit isCommutable = 1; } class IsBranch { bit isBranch = 1; bit isCTI = 1; } class IsReturn { bit isReturn = 1; bit isCTI = 1; } class IsCall { bit isCall = 1; bit isCTI = 1; } class IsTailCall { bit isCall = 1; bit isTerminator = 1; bit isReturn = 1; bit isBarrier = 1; bit hasExtraSrcRegAllocReq = 1; bit isCodeGenOnly = 1; bit isCTI = 1; } class IsAsCheapAsAMove { bit isAsCheapAsAMove = 1; } class NeverHasSideEffects { bit hasSideEffects = 0; } //===----------------------------------------------------------------------===// // Instruction format superclass //===----------------------------------------------------------------------===// include "MipsInstrFormats.td" //===----------------------------------------------------------------------===// // Mips Operand, Complex Patterns and Transformations Definitions. //===----------------------------------------------------------------------===// class ConstantSImmAsmOperandClass Supers = [], int Offset = 0> : AsmOperandClass { let Name = "ConstantSImm" # Bits # "_" # Offset; let RenderMethod = "addConstantSImmOperands<" # Bits # ", " # Offset # ">"; let PredicateMethod = "isConstantSImm<" # Bits # ", " # Offset # ">"; let SuperClasses = Supers; let DiagnosticType = "SImm" # Bits # "_" # Offset; } class SimmLslAsmOperandClass Supers = [], int Shift = 0> : AsmOperandClass { let Name = "Simm" # Bits # "_Lsl" # Shift; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledSImm<" # Bits # ", " # Shift # ">"; let SuperClasses = Supers; let DiagnosticType = "SImm" # Bits # "_Lsl" # Shift; } class ConstantUImmAsmOperandClass Supers = [], int Offset = 0> : AsmOperandClass { let Name = "ConstantUImm" # Bits # "_" # Offset; let RenderMethod = "addConstantUImmOperands<" # Bits # ", " # Offset # ">"; let PredicateMethod = "isConstantUImm<" # Bits # ", " # Offset # ">"; let SuperClasses = Supers; let DiagnosticType = "UImm" # Bits # "_" # Offset; } class ConstantUImmRangeAsmOperandClass Supers = []> : AsmOperandClass { let Name = "ConstantUImmRange" # Bottom # "_" # Top; let RenderMethod = "addImmOperands"; let PredicateMethod = "isConstantUImmRange<" # Bottom # ", " # Top # ">"; let SuperClasses = Supers; let DiagnosticType = "UImmRange" # Bottom # "_" # Top; } class SImmAsmOperandClass Supers = []> : AsmOperandClass { let Name = "SImm" # Bits; let RenderMethod = "addSImmOperands<" # Bits # ">"; let PredicateMethod = "isSImm<" # Bits # ">"; let SuperClasses = Supers; let DiagnosticType = "SImm" # Bits; } class UImmAsmOperandClass Supers = []> : AsmOperandClass { let Name = "UImm" # Bits; let RenderMethod = "addUImmOperands<" # Bits # ">"; let PredicateMethod = "isUImm<" # Bits # ">"; let SuperClasses = Supers; let DiagnosticType = "UImm" # Bits; } // Generic case - only to support certain assembly pseudo instructions. class UImmAnyAsmOperandClass Supers = []> : AsmOperandClass { let Name = "ImmAny"; let RenderMethod = "addConstantUImmOperands<32>"; let PredicateMethod = "isSImm<" # Bits # ">"; let SuperClasses = Supers; let DiagnosticType = "ImmAny"; } // AsmOperandClasses require a strict ordering which is difficult to manage // as a hierarchy. Instead, we use a linear ordering and impose an order that // is in some places arbitrary. // // Here the rules that are in use: // * Wider immediates are a superset of narrower immediates: // uimm4 < uimm5 < uimm6 // * For the same bit-width, unsigned immediates are a superset of signed // immediates:: // simm4 < uimm4 < simm5 < uimm5 // * For the same upper-bound, signed immediates are a superset of unsigned // immediates: // uimm3 < simm4 < uimm4 < simm4 // * Modified immediates are a superset of ordinary immediates: // uimm5 < uimm5_plus1 (1..32) < uimm5_plus32 (32..63) < uimm6 // The term 'superset' starts to break down here since the uimm5_plus* classes // are not true supersets of uimm5 (but they are still subsets of uimm6). // * 'Relaxed' immediates are supersets of the corresponding unsigned immediate. // uimm16 < uimm16_relaxed // * The codeGen pattern type is arbitrarily ordered. // uimm5 < uimm5_64, and uimm5 < vsplat_uimm5 // This is entirely arbitrary. We need an ordering and what we pick is // unimportant since only one is possible for a given mnemonic. def UImm32CoercedAsmOperandClass : UImmAnyAsmOperandClass<33, []> { let Name = "UImm32_Coerced"; let DiagnosticType = "UImm32_Coerced"; } def SImm32RelaxedAsmOperandClass : SImmAsmOperandClass<32, [UImm32CoercedAsmOperandClass]> { let Name = "SImm32_Relaxed"; let PredicateMethod = "isAnyImm<33>"; let DiagnosticType = "SImm32_Relaxed"; } def SImm32AsmOperandClass : SImmAsmOperandClass<32, [SImm32RelaxedAsmOperandClass]>; def ConstantUImm26AsmOperandClass : ConstantUImmAsmOperandClass<26, [SImm32AsmOperandClass]>; def ConstantUImm20AsmOperandClass : ConstantUImmAsmOperandClass<20, [ConstantUImm26AsmOperandClass]>; def ConstantSImm19Lsl2AsmOperandClass : AsmOperandClass { let Name = "SImm19Lsl2"; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledSImm<19, 2>"; let SuperClasses = [ConstantUImm20AsmOperandClass]; let DiagnosticType = "SImm19_Lsl2"; } def UImm16RelaxedAsmOperandClass : UImmAsmOperandClass<16, [ConstantUImm20AsmOperandClass]> { let Name = "UImm16_Relaxed"; let PredicateMethod = "isAnyImm<16>"; let DiagnosticType = "UImm16_Relaxed"; } // Similar to the relaxed classes which take an SImm and render it as // an UImm, this takes a UImm and renders it as an SImm. def UImm16AltRelaxedAsmOperandClass : SImmAsmOperandClass<16, [UImm16RelaxedAsmOperandClass]> { let Name = "UImm16_AltRelaxed"; let PredicateMethod = "isUImm<16>"; let DiagnosticType = "UImm16_AltRelaxed"; } // FIXME: One of these should probably have UImm16AsmOperandClass as the // superclass instead of UImm16RelaxedasmOPerandClass. def UImm16AsmOperandClass : UImmAsmOperandClass<16, [UImm16RelaxedAsmOperandClass]>; def SImm16RelaxedAsmOperandClass : SImmAsmOperandClass<16, [UImm16RelaxedAsmOperandClass]> { let Name = "SImm16_Relaxed"; let PredicateMethod = "isAnyImm<16>"; let DiagnosticType = "SImm16_Relaxed"; } def SImm16AsmOperandClass : SImmAsmOperandClass<16, [SImm16RelaxedAsmOperandClass]>; def ConstantSImm10Lsl3AsmOperandClass : AsmOperandClass { let Name = "SImm10Lsl3"; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledSImm<10, 3>"; let SuperClasses = [SImm16AsmOperandClass]; let DiagnosticType = "SImm10_Lsl3"; } def ConstantSImm10Lsl2AsmOperandClass : AsmOperandClass { let Name = "SImm10Lsl2"; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledSImm<10, 2>"; let SuperClasses = [ConstantSImm10Lsl3AsmOperandClass]; let DiagnosticType = "SImm10_Lsl2"; } def ConstantSImm11AsmOperandClass : ConstantSImmAsmOperandClass<11, [ConstantSImm10Lsl2AsmOperandClass]>; def ConstantSImm10Lsl1AsmOperandClass : AsmOperandClass { let Name = "SImm10Lsl1"; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledSImm<10, 1>"; let SuperClasses = [ConstantSImm11AsmOperandClass]; let DiagnosticType = "SImm10_Lsl1"; } def ConstantUImm10AsmOperandClass : ConstantUImmAsmOperandClass<10, [ConstantSImm10Lsl1AsmOperandClass]>; def ConstantSImm10AsmOperandClass : ConstantSImmAsmOperandClass<10, [ConstantUImm10AsmOperandClass]>; def ConstantSImm9AsmOperandClass : ConstantSImmAsmOperandClass<9, [ConstantSImm10AsmOperandClass]>; def ConstantSImm7Lsl2AsmOperandClass : AsmOperandClass { let Name = "SImm7Lsl2"; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledSImm<7, 2>"; let SuperClasses = [ConstantSImm9AsmOperandClass]; let DiagnosticType = "SImm7_Lsl2"; } def ConstantUImm8AsmOperandClass : ConstantUImmAsmOperandClass<8, [ConstantSImm7Lsl2AsmOperandClass]>; def ConstantUImm7Sub1AsmOperandClass : ConstantUImmAsmOperandClass<7, [ConstantUImm8AsmOperandClass], -1> { // Specify the names since the -1 offset causes invalid identifiers otherwise. let Name = "UImm7_N1"; let DiagnosticType = "UImm7_N1"; } def ConstantUImm7AsmOperandClass : ConstantUImmAsmOperandClass<7, [ConstantUImm7Sub1AsmOperandClass]>; def ConstantUImm6Lsl2AsmOperandClass : AsmOperandClass { let Name = "UImm6Lsl2"; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledUImm<6, 2>"; let SuperClasses = [ConstantUImm7AsmOperandClass]; let DiagnosticType = "UImm6_Lsl2"; } def ConstantUImm6AsmOperandClass : ConstantUImmAsmOperandClass<6, [ConstantUImm6Lsl2AsmOperandClass]>; def ConstantSImm6AsmOperandClass : ConstantSImmAsmOperandClass<6, [ConstantUImm6AsmOperandClass]>; def ConstantUImm5Lsl2AsmOperandClass : AsmOperandClass { let Name = "UImm5Lsl2"; let RenderMethod = "addImmOperands"; let PredicateMethod = "isScaledUImm<5, 2>"; let SuperClasses = [ConstantSImm6AsmOperandClass]; let DiagnosticType = "UImm5_Lsl2"; } def ConstantUImm5_Range2_64AsmOperandClass : ConstantUImmRangeAsmOperandClass<2, 64, [ConstantUImm5Lsl2AsmOperandClass]>; def ConstantUImm5Plus33AsmOperandClass : ConstantUImmAsmOperandClass<5, [ConstantUImm5_Range2_64AsmOperandClass], 33>; def ConstantUImm5ReportUImm6AsmOperandClass : ConstantUImmAsmOperandClass<5, [ConstantUImm5Plus33AsmOperandClass]> { let Name = "ConstantUImm5_0_Report_UImm6"; let DiagnosticType = "UImm5_0_Report_UImm6"; } def ConstantUImm5Plus32AsmOperandClass : ConstantUImmAsmOperandClass< 5, [ConstantUImm5ReportUImm6AsmOperandClass], 32>; def ConstantUImm5Plus32NormalizeAsmOperandClass : ConstantUImmAsmOperandClass<5, [ConstantUImm5Plus32AsmOperandClass], 32> { let Name = "ConstantUImm5_32_Norm"; // We must also subtract 32 when we render the operand. let RenderMethod = "addConstantUImmOperands<5, 32, -32>"; } def ConstantUImm5Plus1ReportUImm6AsmOperandClass : ConstantUImmAsmOperandClass< 5, [ConstantUImm5Plus32NormalizeAsmOperandClass], 1>{ let Name = "ConstantUImm5_Plus1_Report_UImm6"; } def ConstantUImm5Plus1AsmOperandClass : ConstantUImmAsmOperandClass< 5, [ConstantUImm5Plus1ReportUImm6AsmOperandClass], 1>; def ConstantUImm5AsmOperandClass : ConstantUImmAsmOperandClass<5, [ConstantUImm5Plus1AsmOperandClass]>; def ConstantSImm5AsmOperandClass : ConstantSImmAsmOperandClass<5, [ConstantUImm5AsmOperandClass]>; def ConstantUImm4AsmOperandClass : ConstantUImmAsmOperandClass<4, [ConstantSImm5AsmOperandClass]>; def ConstantSImm4AsmOperandClass : ConstantSImmAsmOperandClass<4, [ConstantUImm4AsmOperandClass]>; def ConstantUImm3AsmOperandClass : ConstantUImmAsmOperandClass<3, [ConstantSImm4AsmOperandClass]>; def ConstantUImm2Plus1AsmOperandClass : ConstantUImmAsmOperandClass<2, [ConstantUImm3AsmOperandClass], 1>; def ConstantUImm2AsmOperandClass : ConstantUImmAsmOperandClass<2, [ConstantUImm3AsmOperandClass]>; def ConstantUImm1AsmOperandClass : ConstantUImmAsmOperandClass<1, [ConstantUImm2AsmOperandClass]>; def ConstantImmzAsmOperandClass : AsmOperandClass { let Name = "ConstantImmz"; let RenderMethod = "addConstantUImmOperands<1>"; let PredicateMethod = "isConstantImmz"; let SuperClasses = [ConstantUImm1AsmOperandClass]; let DiagnosticType = "Immz"; } def Simm19Lsl2AsmOperand : SimmLslAsmOperandClass<19, [], 2>; def MipsJumpTargetAsmOperand : AsmOperandClass { let Name = "JumpTarget"; let ParserMethod = "parseJumpTarget"; let PredicateMethod = "isImm"; let RenderMethod = "addImmOperands"; } // Instruction operand types def jmptarget : Operand { let EncoderMethod = "getJumpTargetOpValue"; let ParserMatchClass = MipsJumpTargetAsmOperand; } def brtarget : Operand { let EncoderMethod = "getBranchTargetOpValue"; let OperandType = "OPERAND_PCREL"; let DecoderMethod = "DecodeBranchTarget"; let ParserMatchClass = MipsJumpTargetAsmOperand; } def brtarget1SImm16 : Operand { let EncoderMethod = "getBranchTargetOpValue1SImm16"; let OperandType = "OPERAND_PCREL"; let DecoderMethod = "DecodeBranchTarget1SImm16"; let ParserMatchClass = MipsJumpTargetAsmOperand; } def calltarget : Operand { let EncoderMethod = "getJumpTargetOpValue"; let ParserMatchClass = MipsJumpTargetAsmOperand; } def imm64: Operand; def simm19_lsl2 : Operand { let EncoderMethod = "getSimm19Lsl2Encoding"; let DecoderMethod = "DecodeSimm19Lsl2"; let ParserMatchClass = Simm19Lsl2AsmOperand; } def simm18_lsl3 : Operand { let EncoderMethod = "getSimm18Lsl3Encoding"; let DecoderMethod = "DecodeSimm18Lsl3"; let ParserMatchClass = MipsJumpTargetAsmOperand; } // Zero def uimmz : Operand { let PrintMethod = "printUImm<0>"; let ParserMatchClass = ConstantImmzAsmOperandClass; } // size operand of ins instruction def uimm_range_2_64 : Operand { let PrintMethod = "printUImm<6, 2>"; let EncoderMethod = "getSizeInsEncoding"; let DecoderMethod = "DecodeInsSize"; let ParserMatchClass = ConstantUImm5_Range2_64AsmOperandClass; } // Unsigned Operands foreach I = {1, 2, 3, 4, 5, 6, 7, 8, 10, 20, 26} in def uimm # I : Operand { let PrintMethod = "printUImm<" # I # ">"; let ParserMatchClass = !cast("ConstantUImm" # I # "AsmOperandClass"); } def uimm2_plus1 : Operand { let PrintMethod = "printUImm<2, 1>"; let EncoderMethod = "getUImmWithOffsetEncoding<2, 1>"; let DecoderMethod = "DecodeUImmWithOffset<2, 1>"; let ParserMatchClass = ConstantUImm2Plus1AsmOperandClass; } def uimm5_plus1 : Operand { let PrintMethod = "printUImm<5, 1>"; let EncoderMethod = "getUImmWithOffsetEncoding<5, 1>"; let DecoderMethod = "DecodeUImmWithOffset<5, 1>"; let ParserMatchClass = ConstantUImm5Plus1AsmOperandClass; } def uimm5_plus1_report_uimm6 : Operand { let PrintMethod = "printUImm<6, 1>"; let EncoderMethod = "getUImmWithOffsetEncoding<5, 1>"; let DecoderMethod = "DecodeUImmWithOffset<5, 1>"; let ParserMatchClass = ConstantUImm5Plus1ReportUImm6AsmOperandClass; } def uimm5_plus32 : Operand { let PrintMethod = "printUImm<5, 32>"; let ParserMatchClass = ConstantUImm5Plus32AsmOperandClass; } def uimm5_plus33 : Operand { let PrintMethod = "printUImm<5, 33>"; let EncoderMethod = "getUImmWithOffsetEncoding<5, 1>"; let DecoderMethod = "DecodeUImmWithOffset<5, 1>"; let ParserMatchClass = ConstantUImm5Plus33AsmOperandClass; } def uimm5_inssize_plus1 : Operand { let PrintMethod = "printUImm<6>"; let ParserMatchClass = ConstantUImm5Plus1AsmOperandClass; let EncoderMethod = "getSizeInsEncoding"; let DecoderMethod = "DecodeInsSize"; } def uimm5_plus32_normalize : Operand { let PrintMethod = "printUImm<5>"; let ParserMatchClass = ConstantUImm5Plus32NormalizeAsmOperandClass; } def uimm5_lsl2 : Operand { let EncoderMethod = "getUImm5Lsl2Encoding"; let DecoderMethod = "DecodeUImmWithOffsetAndScale<5, 0, 4>"; let ParserMatchClass = ConstantUImm5Lsl2AsmOperandClass; } def uimm5_plus32_normalize_64 : Operand { let PrintMethod = "printUImm<5>"; let ParserMatchClass = ConstantUImm5Plus32NormalizeAsmOperandClass; } def uimm6_lsl2 : Operand { let EncoderMethod = "getUImm6Lsl2Encoding"; let DecoderMethod = "DecodeUImmWithOffsetAndScale<6, 0, 4>"; let ParserMatchClass = ConstantUImm6Lsl2AsmOperandClass; } foreach I = {16} in def uimm # I : Operand { let PrintMethod = "printUImm<" # I # ">"; let ParserMatchClass = !cast("UImm" # I # "AsmOperandClass"); } // Like uimm16_64 but coerces simm16 to uimm16. def uimm16_relaxed : Operand { let PrintMethod = "printUImm<16>"; let ParserMatchClass = !cast("UImm16RelaxedAsmOperandClass"); } foreach I = {5} in def uimm # I # _64 : Operand { let PrintMethod = "printUImm<" # I # ">"; let ParserMatchClass = !cast("ConstantUImm" # I # "AsmOperandClass"); } foreach I = {16} in def uimm # I # _64 : Operand { let PrintMethod = "printUImm<" # I # ">"; let ParserMatchClass = !cast("UImm" # I # "AsmOperandClass"); } // Like uimm16_64 but coerces simm16 to uimm16. def uimm16_64_relaxed : Operand { let PrintMethod = "printUImm<16>"; let ParserMatchClass = !cast("UImm16RelaxedAsmOperandClass"); } def uimm16_altrelaxed : Operand { let PrintMethod = "printUImm<16>"; let ParserMatchClass = !cast("UImm16AltRelaxedAsmOperandClass"); } // Like uimm5 but reports a less confusing error for 32-63 when // an instruction alias permits that. def uimm5_report_uimm6 : Operand { let PrintMethod = "printUImm<6>"; let ParserMatchClass = ConstantUImm5ReportUImm6AsmOperandClass; } // Like uimm5_64 but reports a less confusing error for 32-63 when // an instruction alias permits that. def uimm5_64_report_uimm6 : Operand { let PrintMethod = "printUImm<5>"; let ParserMatchClass = ConstantUImm5ReportUImm6AsmOperandClass; } foreach I = {1, 2, 3, 4} in def uimm # I # _ptr : Operand { let PrintMethod = "printUImm<" # I # ">"; let ParserMatchClass = !cast("ConstantUImm" # I # "AsmOperandClass"); } foreach I = {1, 2, 3, 4, 5, 6, 8} in def vsplat_uimm # I : Operand { let PrintMethod = "printUImm<" # I # ">"; let ParserMatchClass = !cast("ConstantUImm" # I # "AsmOperandClass"); } // Signed operands foreach I = {4, 5, 6, 9, 10, 11} in def simm # I : Operand { let DecoderMethod = "DecodeSImmWithOffsetAndScale<" # I # ">"; let ParserMatchClass = !cast("ConstantSImm" # I # "AsmOperandClass"); } foreach I = {1, 2, 3} in def simm10_lsl # I : Operand { let DecoderMethod = "DecodeSImmWithOffsetAndScale<10, " # I # ">"; let ParserMatchClass = !cast("ConstantSImm10Lsl" # I # "AsmOperandClass"); } foreach I = {10} in def simm # I # _64 : Operand { let DecoderMethod = "DecodeSImmWithOffsetAndScale<" # I # ">"; let ParserMatchClass = !cast("ConstantSImm" # I # "AsmOperandClass"); } foreach I = {5, 10} in def vsplat_simm # I : Operand { let ParserMatchClass = !cast("ConstantSImm" # I # "AsmOperandClass"); } def simm7_lsl2 : Operand { let EncoderMethod = "getSImm7Lsl2Encoding"; let DecoderMethod = "DecodeSImmWithOffsetAndScale<" # I # ", 0, 4>"; let ParserMatchClass = ConstantSImm7Lsl2AsmOperandClass; } foreach I = {16, 32} in def simm # I : Operand { let DecoderMethod = "DecodeSImmWithOffsetAndScale<" # I # ">"; let ParserMatchClass = !cast("SImm" # I # "AsmOperandClass"); } // Like simm16 but coerces uimm16 to simm16. def simm16_relaxed : Operand { let DecoderMethod = "DecodeSImmWithOffsetAndScale<16>"; let ParserMatchClass = !cast("SImm16RelaxedAsmOperandClass"); } def simm16_64 : Operand { let DecoderMethod = "DecodeSImmWithOffsetAndScale<16>"; let ParserMatchClass = !cast("SImm16AsmOperandClass"); } // like simm32 but coerces simm32 to uimm32. def uimm32_coerced : Operand { let ParserMatchClass = !cast("UImm32CoercedAsmOperandClass"); } // Like simm32 but coerces uimm32 to simm32. def simm32_relaxed : Operand { let DecoderMethod = "DecodeSImmWithOffsetAndScale<32>"; let ParserMatchClass = !cast("SImm32RelaxedAsmOperandClass"); } // This is almost the same as a uimm7 but 0x7f is interpreted as -1. def li16_imm : Operand { let DecoderMethod = "DecodeLi16Imm"; let ParserMatchClass = ConstantUImm7Sub1AsmOperandClass; } def MipsMemAsmOperand : AsmOperandClass { let Name = "Mem"; let ParserMethod = "parseMemOperand"; } def MipsMemSimm9AsmOperand : AsmOperandClass { let Name = "MemOffsetSimm9"; let SuperClasses = [MipsMemAsmOperand]; let RenderMethod = "addMemOperands"; let ParserMethod = "parseMemOperand"; let PredicateMethod = "isMemWithSimmOffset<9>"; let DiagnosticType = "MemSImm9"; } def MipsMemSimm10AsmOperand : AsmOperandClass { let Name = "MemOffsetSimm10"; let SuperClasses = [MipsMemAsmOperand]; let RenderMethod = "addMemOperands"; let ParserMethod = "parseMemOperand"; let PredicateMethod = "isMemWithSimmOffset<10>"; let DiagnosticType = "MemSImm10"; } def MipsMemSimm12AsmOperand : AsmOperandClass { let Name = "MemOffsetSimm12"; let SuperClasses = [MipsMemAsmOperand]; let RenderMethod = "addMemOperands"; let ParserMethod = "parseMemOperand"; let PredicateMethod = "isMemWithSimmOffset<12>"; let DiagnosticType = "MemSImm12"; } foreach I = {1, 2, 3} in def MipsMemSimm10Lsl # I # AsmOperand : AsmOperandClass { let Name = "MemOffsetSimm10_" # I; let SuperClasses = [MipsMemAsmOperand]; let RenderMethod = "addMemOperands"; let ParserMethod = "parseMemOperand"; let PredicateMethod = "isMemWithSimmOffset<10, " # I # ">"; let DiagnosticType = "MemSImm10Lsl" # I; } def MipsMemSimm11AsmOperand : AsmOperandClass { let Name = "MemOffsetSimm11"; let SuperClasses = [MipsMemAsmOperand]; let RenderMethod = "addMemOperands"; let ParserMethod = "parseMemOperand"; let PredicateMethod = "isMemWithSimmOffset<11>"; let DiagnosticType = "MemSImm11"; } def MipsMemSimm16AsmOperand : AsmOperandClass { let Name = "MemOffsetSimm16"; let SuperClasses = [MipsMemAsmOperand]; let RenderMethod = "addMemOperands"; let ParserMethod = "parseMemOperand"; let PredicateMethod = "isMemWithSimmOffset<16>"; let DiagnosticType = "MemSImm16"; } def MipsMemSimmPtrAsmOperand : AsmOperandClass { let Name = "MemOffsetSimmPtr"; let SuperClasses = [MipsMemAsmOperand]; let RenderMethod = "addMemOperands"; let ParserMethod = "parseMemOperand"; let PredicateMethod = "isMemWithPtrSizeOffset"; let DiagnosticType = "MemSImmPtr"; } def MipsInvertedImmoperand : AsmOperandClass { let Name = "InvNum"; let RenderMethod = "addImmOperands"; let ParserMethod = "parseInvNum"; } def InvertedImOperand : Operand { let ParserMatchClass = MipsInvertedImmoperand; } def InvertedImOperand64 : Operand { let ParserMatchClass = MipsInvertedImmoperand; } class mem_generic : Operand { let PrintMethod = "printMemOperand"; let MIOperandInfo = (ops ptr_rc, simm16); let EncoderMethod = "getMemEncoding"; let ParserMatchClass = MipsMemAsmOperand; let OperandType = "OPERAND_MEMORY"; } // Address operand def mem : mem_generic; // MSA specific address operand def mem_msa : mem_generic { let MIOperandInfo = (ops ptr_rc, simm10); let EncoderMethod = "getMSAMemEncoding"; } def simm12 : Operand { let DecoderMethod = "DecodeSimm12"; } def mem_simm9 : mem_generic { let MIOperandInfo = (ops ptr_rc, simm9); let EncoderMethod = "getMemEncoding"; let ParserMatchClass = MipsMemSimm9AsmOperand; } def mem_simm10 : mem_generic { let MIOperandInfo = (ops ptr_rc, simm10); let EncoderMethod = "getMemEncoding"; let ParserMatchClass = MipsMemSimm10AsmOperand; } foreach I = {1, 2, 3} in def mem_simm10_lsl # I : mem_generic { let MIOperandInfo = (ops ptr_rc, !cast("simm10_lsl" # I)); let EncoderMethod = "getMemEncoding<" # I # ">"; let ParserMatchClass = !cast("MipsMemSimm10Lsl" # I # "AsmOperand"); } def mem_simm11 : mem_generic { let MIOperandInfo = (ops ptr_rc, simm11); let EncoderMethod = "getMemEncoding"; let ParserMatchClass = MipsMemSimm11AsmOperand; } def mem_simm12 : mem_generic { let MIOperandInfo = (ops ptr_rc, simm12); let EncoderMethod = "getMemEncoding"; let ParserMatchClass = MipsMemSimm12AsmOperand; } def mem_simm16 : mem_generic { let MIOperandInfo = (ops ptr_rc, simm16); let EncoderMethod = "getMemEncoding"; let ParserMatchClass = MipsMemSimm16AsmOperand; } def mem_simmptr : mem_generic { let ParserMatchClass = MipsMemSimmPtrAsmOperand; } def mem_ea : Operand { let PrintMethod = "printMemOperandEA"; let MIOperandInfo = (ops ptr_rc, simm16); let EncoderMethod = "getMemEncoding"; let OperandType = "OPERAND_MEMORY"; } def PtrRC : Operand { let MIOperandInfo = (ops ptr_rc); let DecoderMethod = "DecodePtrRegisterClass"; let ParserMatchClass = GPR32AsmOperand; } // size operand of ins instruction def size_ins : Operand { let EncoderMethod = "getSizeInsEncoding"; let DecoderMethod = "DecodeInsSize"; } // Transformation Function - get the lower 16 bits. def LO16 : SDNodeXFormgetZExtValue() & 0xFFFF); }]>; // Transformation Function - get the higher 16 bits. def HI16 : SDNodeXFormgetZExtValue() >> 16) & 0xFFFF); }]>; // Plus 1. def Plus1 : SDNodeXFormgetSExtValue() + 1); }]>; // Node immediate is zero (e.g. insve.d) def immz : PatLeaf<(imm), [{ return N->getSExtValue() == 0; }]>; // Node immediate fits as 16-bit sign extended on target immediate. // e.g. addi, andi def immSExt8 : PatLeaf<(imm), [{ return isInt<8>(N->getSExtValue()); }]>; // Node immediate fits as 16-bit sign extended on target immediate. // e.g. addi, andi def immSExt16 : PatLeaf<(imm), [{ return isInt<16>(N->getSExtValue()); }]>; // Node immediate fits as 7-bit zero extended on target immediate. def immZExt7 : PatLeaf<(imm), [{ return isUInt<7>(N->getZExtValue()); }]>; // Node immediate fits as 16-bit zero extended on target immediate. // The LO16 param means that only the lower 16 bits of the node // immediate are caught. // e.g. addiu, sltiu def immZExt16 : PatLeaf<(imm), [{ if (N->getValueType(0) == MVT::i32) return (uint32_t)N->getZExtValue() == (unsigned short)N->getZExtValue(); else return (uint64_t)N->getZExtValue() == (unsigned short)N->getZExtValue(); }], LO16>; // Immediate can be loaded with LUi (32-bit int with lower 16-bit cleared). def immSExt32Low16Zero : PatLeaf<(imm), [{ int64_t Val = N->getSExtValue(); return isInt<32>(Val) && !(Val & 0xffff); }]>; // Zero-extended 32-bit unsigned int with lower 16-bit cleared. def immZExt32Low16Zero : PatLeaf<(imm), [{ uint64_t Val = N->getZExtValue(); return isUInt<32>(Val) && !(Val & 0xffff); }]>; // Note immediate fits as a 32 bit signed extended on target immediate. def immSExt32 : PatLeaf<(imm), [{ return isInt<32>(N->getSExtValue()); }]>; // Note immediate fits as a 32 bit zero extended on target immediate. def immZExt32 : PatLeaf<(imm), [{ return isUInt<32>(N->getZExtValue()); }]>; // shamt field must fit in 5 bits. def immZExt5 : ImmLeaf; def immZExt5Plus1 : PatLeaf<(imm), [{ return isUInt<5>(N->getZExtValue() - 1); }]>; def immZExt5Plus32 : PatLeaf<(imm), [{ return isUInt<5>(N->getZExtValue() - 32); }]>; def immZExt5Plus33 : PatLeaf<(imm), [{ return isUInt<5>(N->getZExtValue() - 33); }]>; def immZExt5To31 : SDNodeXFormgetZExtValue()); }]>; // True if (N + 1) fits in 16-bit field. def immSExt16Plus1 : PatLeaf<(imm), [{ return isInt<17>(N->getSExtValue()) && isInt<16>(N->getSExtValue() + 1); }]>; def immZExtRange2To64 : PatLeaf<(imm), [{ return isUInt<7>(N->getZExtValue()) && (N->getZExtValue() >= 2) && (N->getZExtValue() <= 64); }]>; def ORiPred : PatLeaf<(imm), [{ return isUInt<16>(N->getZExtValue()) && !isInt<16>(N->getSExtValue()); }], LO16>; def LUiPred : PatLeaf<(imm), [{ int64_t Val = N->getSExtValue(); return !isInt<16>(Val) && isInt<32>(Val) && !(Val & 0xffff); }]>; def LUiORiPred : PatLeaf<(imm), [{ int64_t SVal = N->getSExtValue(); return isInt<32>(SVal) && (SVal & 0xffff); }]>; // Mips Address Mode! SDNode frameindex could possibily be a match // since load and store instructions from stack used it. def addr : ComplexPattern; def addrRegImm : ComplexPattern; def addrDefault : ComplexPattern; def addrimm10 : ComplexPattern; def addrimm10lsl1 : ComplexPattern; def addrimm10lsl2 : ComplexPattern; def addrimm10lsl3 : ComplexPattern; //===----------------------------------------------------------------------===// // Instructions specific format //===----------------------------------------------------------------------===// // Arithmetic and logical instructions with 3 register operands. class ArithLogicR: InstSE<(outs RO:$rd), (ins RO:$rs, RO:$rt), !strconcat(opstr, "\t$rd, $rs, $rt"), [(set RO:$rd, (OpNode RO:$rs, RO:$rt))], Itin, FrmR, opstr> { let isCommutable = isComm; let isReMaterializable = 1; let TwoOperandAliasConstraint = "$rd = $rs"; } // Arithmetic and logical instructions with 2 register operands. class ArithLogicI : InstSE<(outs RO:$rt), (ins RO:$rs, Od:$imm16), !strconcat(opstr, "\t$rt, $rs, $imm16"), [(set RO:$rt, (OpNode RO:$rs, imm_type:$imm16))], Itin, FrmI, opstr> { let isReMaterializable = 1; let TwoOperandAliasConstraint = "$rs = $rt"; } // Arithmetic Multiply ADD/SUB class MArithR : InstSE<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt), !strconcat(opstr, "\t$rs, $rt"), [], itin, FrmR, opstr> { let Defs = [HI0, LO0]; let Uses = [HI0, LO0]; let isCommutable = isComm; } // Logical class LogicNOR: InstSE<(outs RO:$rd), (ins RO:$rs, RO:$rt), !strconcat(opstr, "\t$rd, $rs, $rt"), [(set RO:$rd, (not (or RO:$rs, RO:$rt)))], II_NOR, FrmR, opstr> { let isCommutable = 1; } // Shifts class shift_rotate_imm : InstSE<(outs RO:$rd), (ins RO:$rt, ImmOpnd:$shamt), !strconcat(opstr, "\t$rd, $rt, $shamt"), [(set RO:$rd, (OpNode RO:$rt, PF:$shamt))], itin, FrmR, opstr> { let TwoOperandAliasConstraint = "$rt = $rd"; } class shift_rotate_reg: InstSE<(outs RO:$rd), (ins RO:$rt, GPR32Opnd:$rs), !strconcat(opstr, "\t$rd, $rt, $rs"), [(set RO:$rd, (OpNode RO:$rt, GPR32Opnd:$rs))], itin, FrmR, opstr>; // Load Upper Immediate class LoadUpper: InstSE<(outs RO:$rt), (ins Imm:$imm16), !strconcat(opstr, "\t$rt, $imm16"), [], II_LUI, FrmI, opstr>, IsAsCheapAsAMove { let hasSideEffects = 0; let isReMaterializable = 1; } // Memory Load/Store class LoadMemory : InstSE<(outs RO:$rt), (ins MO:$addr), !strconcat(opstr, "\t$rt, $addr"), [(set RO:$rt, (OpNode Addr:$addr))], Itin, FrmI, opstr> { let DecoderMethod = "DecodeMem"; let canFoldAsLoad = 1; string BaseOpcode = opstr; let mayLoad = 1; } class Load : LoadMemory; class StoreMemory : InstSE<(outs), (ins RO:$rt, MO:$addr), !strconcat(opstr, "\t$rt, $addr"), [(OpNode RO:$rt, Addr:$addr)], Itin, FrmI, opstr> { let DecoderMethod = "DecodeMem"; string BaseOpcode = opstr; let mayStore = 1; } class Store : StoreMemory; // Load/Store Left/Right let canFoldAsLoad = 1 in class LoadLeftRight : InstSE<(outs RO:$rt), (ins mem:$addr, RO:$src), !strconcat(opstr, "\t$rt, $addr"), [(set RO:$rt, (OpNode addr:$addr, RO:$src))], Itin, FrmI> { let DecoderMethod = "DecodeMem"; string Constraints = "$src = $rt"; let BaseOpcode = opstr; } class StoreLeftRight : InstSE<(outs), (ins RO:$rt, mem:$addr), !strconcat(opstr, "\t$rt, $addr"), [(OpNode RO:$rt, addr:$addr)], Itin, FrmI> { let DecoderMethod = "DecodeMem"; let BaseOpcode = opstr; } // COP2 Load/Store class LW_FT2 : InstSE<(outs RC:$rt), (ins mem_simm16:$addr), !strconcat(opstr, "\t$rt, $addr"), [(set RC:$rt, (OpNode addrDefault:$addr))], Itin, FrmFI, opstr> { let DecoderMethod = "DecodeFMem2"; let mayLoad = 1; } class SW_FT2 : InstSE<(outs), (ins RC:$rt, mem_simm16:$addr), !strconcat(opstr, "\t$rt, $addr"), [(OpNode RC:$rt, addrDefault:$addr)], Itin, FrmFI, opstr> { let DecoderMethod = "DecodeFMem2"; let mayStore = 1; } // COP3 Load/Store class LW_FT3 : InstSE<(outs RC:$rt), (ins mem:$addr), !strconcat(opstr, "\t$rt, $addr"), [(set RC:$rt, (OpNode addrDefault:$addr))], Itin, FrmFI, opstr> { let DecoderMethod = "DecodeFMem3"; let mayLoad = 1; } class SW_FT3 : InstSE<(outs), (ins RC:$rt, mem:$addr), !strconcat(opstr, "\t$rt, $addr"), [(OpNode RC:$rt, addrDefault:$addr)], Itin, FrmFI, opstr> { let DecoderMethod = "DecodeFMem3"; let mayStore = 1; } // Conditional Branch class CBranch : InstSE<(outs), (ins RO:$rs, RO:$rt, opnd:$offset), !strconcat(opstr, "\t$rs, $rt, $offset"), [(brcond (i32 (cond_op RO:$rs, RO:$rt)), bb:$offset)], II_BCC, FrmI, opstr> { let isBranch = 1; let isTerminator = 1; let hasDelaySlot = 1; let Defs = [AT]; bit isCTI = 1; } class CBranchLikely : InstSE<(outs), (ins RO:$rs, RO:$rt, opnd:$offset), !strconcat(opstr, "\t$rs, $rt, $offset"), [], II_BCC, FrmI, opstr> { let isBranch = 1; let isTerminator = 1; let hasDelaySlot = 1; let Defs = [AT]; bit isCTI = 1; } class CBranchZero : InstSE<(outs), (ins RO:$rs, opnd:$offset), !strconcat(opstr, "\t$rs, $offset"), [(brcond (i32 (cond_op RO:$rs, 0)), bb:$offset)], II_BCCZ, FrmI, opstr> { let isBranch = 1; let isTerminator = 1; let hasDelaySlot = 1; let Defs = [AT]; bit isCTI = 1; } class CBranchZeroLikely : InstSE<(outs), (ins RO:$rs, opnd:$offset), !strconcat(opstr, "\t$rs, $offset"), [], II_BCCZ, FrmI, opstr> { let isBranch = 1; let isTerminator = 1; let hasDelaySlot = 1; let Defs = [AT]; bit isCTI = 1; } // SetCC class SetCC_R : InstSE<(outs GPR32Opnd:$rd), (ins RO:$rs, RO:$rt), !strconcat(opstr, "\t$rd, $rs, $rt"), [(set GPR32Opnd:$rd, (cond_op RO:$rs, RO:$rt))], II_SLT_SLTU, FrmR, opstr>; class SetCC_I: InstSE<(outs GPR32Opnd:$rt), (ins RO:$rs, Od:$imm16), !strconcat(opstr, "\t$rt, $rs, $imm16"), [(set GPR32Opnd:$rt, (cond_op RO:$rs, imm_type:$imm16))], II_SLTI_SLTIU, FrmI, opstr>; // Jump class JumpFJ : InstSE<(outs), (ins opnd:$target), !strconcat(opstr, "\t$target"), [(operator targetoperator:$target)], II_J, FrmJ, bopstr> { let isTerminator=1; let isBarrier=1; let hasDelaySlot = 1; let DecoderMethod = "DecodeJumpTarget"; let Defs = [AT]; bit isCTI = 1; } // Unconditional branch class UncondBranch : PseudoSE<(outs), (ins brtarget:$offset), [(br bb:$offset)], II_B>, PseudoInstExpansion<(BEQInst ZERO, ZERO, opnd:$offset)> { let isBranch = 1; let isTerminator = 1; let isBarrier = 1; let hasDelaySlot = 1; let AdditionalPredicates = [RelocPIC]; let Defs = [AT]; bit isCTI = 1; } // Base class for indirect branch and return instruction classes. let isTerminator=1, isBarrier=1, hasDelaySlot = 1, isCTI = 1 in class JumpFR: InstSE<(outs), (ins RO:$rs), "jr\t$rs", [(operator RO:$rs)], II_JR, FrmR, opstr>; // Indirect branch class IndirectBranch : JumpFR { let isBranch = 1; let isIndirectBranch = 1; } // Jump and Link (Call) let isCall=1, hasDelaySlot=1, isCTI=1, Defs = [RA] in { class JumpLink : InstSE<(outs), (ins opnd:$target), !strconcat(opstr, "\t$target"), [(MipsJmpLink tglobaladdr:$target)], II_JAL, FrmJ, opstr> { let DecoderMethod = "DecodeJumpTarget"; } class JumpLinkRegPseudo: PseudoSE<(outs), (ins RO:$rs), [(MipsJmpLink RO:$rs)], II_JALR>, PseudoInstExpansion<(JALRInst RetReg, ResRO:$rs)>; class JumpLinkReg: InstSE<(outs RO:$rd), (ins RO:$rs), !strconcat(opstr, "\t$rd, $rs"), [], II_JALR, FrmR, opstr>; class BGEZAL_FT : InstSE<(outs), (ins RO:$rs, opnd:$offset), !strconcat(opstr, "\t$rs, $offset"), [], II_BCCZAL, FrmI, opstr> { let hasDelaySlot = 1; } } let isCall = 1, isTerminator = 1, isReturn = 1, isBarrier = 1, hasDelaySlot = 1, hasExtraSrcRegAllocReq = 1, isCTI = 1, Defs = [AT] in { class TailCall : PseudoSE<(outs), (ins calltarget:$target), [], II_J>, PseudoInstExpansion<(JumpInst Opnd:$target)>; class TailCallReg : PseudoSE<(outs), (ins RO:$rs), [(MipsTailCall RO:$rs)], II_JR>, PseudoInstExpansion<(JumpInst RO:$rs)>; } class BAL_BR_Pseudo : PseudoSE<(outs), (ins opnd:$offset), [], II_BCCZAL>, PseudoInstExpansion<(RealInst ZERO, opnd:$offset)> { let isBranch = 1; let isTerminator = 1; let isBarrier = 1; let hasDelaySlot = 1; let Defs = [RA]; bit isCTI = 1; } let isCTI = 1 in { // Syscall class SYS_FT : InstSE<(outs), (ins ImmOp:$code_), !strconcat(opstr, "\t$code_"), [], itin, FrmI, opstr>; // Break class BRK_FT : InstSE<(outs), (ins uimm10:$code_1, uimm10:$code_2), !strconcat(opstr, "\t$code_1, $code_2"), [], II_BREAK, FrmOther, opstr>; // (D)Eret class ER_FT : InstSE<(outs), (ins), opstr, [], itin, FrmOther, opstr>; // Wait class WAIT_FT : InstSE<(outs), (ins), opstr, [], II_WAIT, FrmOther, opstr>; } // Interrupts class DEI_FT : InstSE<(outs RO:$rt), (ins), !strconcat(opstr, "\t$rt"), [], itin, FrmOther, opstr>; // Sync let hasSideEffects = 1 in class SYNC_FT : InstSE<(outs), (ins uimm5:$stype), "sync $stype", [(MipsSync immZExt5:$stype)], II_SYNC, FrmOther, opstr>; class SYNCI_FT : InstSE<(outs), (ins MO:$addr), !strconcat(opstr, "\t$addr"), [], II_SYNCI, FrmOther, opstr> { let hasSideEffects = 1; let DecoderMethod = "DecodeSyncI"; } let hasSideEffects = 1, isCTI = 1 in { class TEQ_FT : InstSE<(outs), (ins RO:$rs, RO:$rt, ImmOp:$code_), !strconcat(opstr, "\t$rs, $rt, $code_"), [], itin, FrmI, opstr>; class TEQI_FT : InstSE<(outs), (ins RO:$rs, simm16:$imm16), !strconcat(opstr, "\t$rs, $imm16"), [], itin, FrmOther, opstr>; } // Mul, Div class Mult DefRegs> : InstSE<(outs), (ins RO:$rs, RO:$rt), !strconcat(opstr, "\t$rs, $rt"), [], itin, FrmR, opstr> { let isCommutable = 1; let Defs = DefRegs; let hasSideEffects = 0; } // Pseudo multiply/divide instruction with explicit accumulator register // operands. class MultDivPseudo : PseudoSE<(outs R0:$ac), (ins R1:$rs, R1:$rt), [(set R0:$ac, (OpNode R1:$rs, R1:$rt))], Itin>, PseudoInstExpansion<(RealInst R1:$rs, R1:$rt)> { let isCommutable = IsComm; let hasSideEffects = HasSideEffects; let usesCustomInserter = UsesCustomInserter; } // Pseudo multiply add/sub instruction with explicit accumulator register // operands. class MAddSubPseudo : PseudoSE<(outs ACC64:$ac), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, ACC64:$acin), [(set ACC64:$ac, (OpNode GPR32Opnd:$rs, GPR32Opnd:$rt, ACC64:$acin))], itin>, PseudoInstExpansion<(RealInst GPR32Opnd:$rs, GPR32Opnd:$rt)> { string Constraints = "$acin = $ac"; } class Div DefRegs> : InstSE<(outs), (ins RO:$rs, RO:$rt), !strconcat(opstr, "\t$$zero, $rs, $rt"), [], itin, FrmR, opstr> { let Defs = DefRegs; } // Move from Hi/Lo class PseudoMFLOHI : PseudoSE<(outs DstRC:$rd), (ins SrcRC:$hilo), [(set DstRC:$rd, (OpNode SrcRC:$hilo))], II_MFHI_MFLO>; class MoveFromLOHI: InstSE<(outs RO:$rd), (ins), !strconcat(opstr, "\t$rd"), [], II_MFHI_MFLO, FrmR, opstr> { let Uses = [UseReg]; let hasSideEffects = 0; let isMoveReg = 1; } class PseudoMTLOHI : PseudoSE<(outs DstRC:$lohi), (ins SrcRC:$lo, SrcRC:$hi), [(set DstRC:$lohi, (MipsMTLOHI SrcRC:$lo, SrcRC:$hi))], II_MTHI_MTLO>; class MoveToLOHI DefRegs>: InstSE<(outs), (ins RO:$rs), !strconcat(opstr, "\t$rs"), [], II_MTHI_MTLO, FrmR, opstr> { let Defs = DefRegs; let hasSideEffects = 0; let isMoveReg = 1; } class EffectiveAddress : InstSE<(outs RO:$rt), (ins mem_ea:$addr), !strconcat(opstr, "\t$rt, $addr"), [(set RO:$rt, addr:$addr)], II_ADDIU, FrmI, !strconcat(opstr, "_lea")> { let isCodeGenOnly = 1; let hasNoSchedulingInfo = 1; let DecoderMethod = "DecodeMem"; } // Count Leading Ones/Zeros in Word class CountLeading0: InstSE<(outs RO:$rd), (ins RO:$rs), !strconcat(opstr, "\t$rd, $rs"), [(set RO:$rd, (ctlz RO:$rs))], itin, FrmR, opstr>; class CountLeading1: InstSE<(outs RO:$rd), (ins RO:$rs), !strconcat(opstr, "\t$rd, $rs"), [(set RO:$rd, (ctlz (not RO:$rs)))], itin, FrmR, opstr>; // Sign Extend in Register. class SignExtInReg : InstSE<(outs RO:$rd), (ins RO:$rt), !strconcat(opstr, "\t$rd, $rt"), [(set RO:$rd, (sext_inreg RO:$rt, vt))], itin, FrmR, opstr>; // Subword Swap class SubwordSwap: InstSE<(outs RO:$rd), (ins RO:$rt), !strconcat(opstr, "\t$rd, $rt"), [], itin, FrmR, opstr> { let hasSideEffects = 0; } // Read Hardware class ReadHardware : InstSE<(outs CPURegOperand:$rt), (ins RO:$rd, uimm8:$sel), "rdhwr\t$rt, $rd, $sel", [], II_RDHWR, FrmR, "rdhwr">; // Ext and Ins class ExtBase : InstSE<(outs RO:$rt), (ins RO:$rs, PosOpnd:$pos, SizeOpnd:$size), !strconcat(opstr, "\t$rt, $rs, $pos, $size"), [(set RO:$rt, (Op RO:$rs, PosImm:$pos, SizeImm:$size))], II_EXT, FrmR, opstr>; // 'ins' and its' 64 bit variants are matched by C++ code. class InsBase: InstSE<(outs RO:$rt), (ins RO:$rs, PosOpnd:$pos, SizeOpnd:$size, RO:$src), !strconcat(opstr, "\t$rt, $rs, $pos, $size"), [(set RO:$rt, (null_frag RO:$rs, PosImm:$pos, SizeImm:$size, RO:$src))], II_INS, FrmR, opstr> { let Constraints = "$src = $rt"; } // Atomic instructions with 2 source operands (ATOMIC_SWAP & ATOMIC_LOAD_*). class Atomic2Ops : PseudoSE<(outs DRC:$dst), (ins PtrRC:$ptr, DRC:$incr), [(set DRC:$dst, (Op iPTR:$ptr, DRC:$incr))]>; +class Atomic2OpsPostRA : + PseudoSE<(outs RC:$dst), (ins PtrRC:$ptr, RC:$incr), []> { + let mayLoad = 1; + let mayStore = 1; +} + +class Atomic2OpsSubwordPostRA : + PseudoSE<(outs RC:$dst), (ins PtrRC:$ptr, RC:$incr, RC:$mask, RC:$mask2, + RC:$shiftamnt), []>; + // Atomic Compare & Swap. +// Atomic compare and swap is lowered into two stages. The first stage happens +// during ISelLowering, which produces the PostRA version of this instruction. class AtomicCmpSwap : PseudoSE<(outs DRC:$dst), (ins PtrRC:$ptr, DRC:$cmp, DRC:$swap), [(set DRC:$dst, (Op iPTR:$ptr, DRC:$cmp, DRC:$swap))]>; +class AtomicCmpSwapPostRA : + PseudoSE<(outs RC:$dst), (ins PtrRC:$ptr, RC:$cmp, RC:$swap), []> { + let mayLoad = 1; + let mayStore = 1; +} + +class AtomicCmpSwapSubwordPostRA : + PseudoSE<(outs RC:$dst), (ins PtrRC:$ptr, RC:$mask, RC:$ShiftCmpVal, + RC:$mask2, RC:$ShiftNewVal, RC:$ShiftAmt), []> { + let mayLoad = 1; + let mayStore = 1; +} + + class LLBase : InstSE<(outs RO:$rt), (ins MO:$addr), !strconcat(opstr, "\t$rt, $addr"), [], II_LL, FrmI, opstr> { let DecoderMethod = "DecodeMem"; let mayLoad = 1; } class SCBase : InstSE<(outs RO:$dst), (ins RO:$rt, mem:$addr), !strconcat(opstr, "\t$rt, $addr"), [], II_SC, FrmI> { let DecoderMethod = "DecodeMem"; let mayStore = 1; let Constraints = "$rt = $dst"; } class MFC3OP : InstSE<(outs RO:$rt), (ins RD:$rd, uimm3:$sel), !strconcat(asmstr, "\t$rt, $rd, $sel"), [], itin, FrmFR> { let BaseOpcode = asmstr; } class MTC3OP : InstSE<(outs RO:$rd), (ins RD:$rt, uimm3:$sel), !strconcat(asmstr, "\t$rt, $rd, $sel"), [], itin, FrmFR> { let BaseOpcode = asmstr; } class TrapBase : PseudoSE<(outs), (ins), [(trap)], II_TRAP>, PseudoInstExpansion<(RealInst 0, 0)> { let isBarrier = 1; let isTerminator = 1; let isCodeGenOnly = 1; let isCTI = 1; } //===----------------------------------------------------------------------===// // Pseudo instructions //===----------------------------------------------------------------------===// // Return RA. let isReturn=1, isTerminator=1, isBarrier=1, hasCtrlDep=1, isCTI=1 in { let hasDelaySlot=1 in def RetRA : PseudoSE<(outs), (ins), [(MipsRet)]>; let hasSideEffects=1 in def ERet : PseudoSE<(outs), (ins), [(MipsERet)]>; } let Defs = [SP], Uses = [SP], hasSideEffects = 1 in { def ADJCALLSTACKDOWN : MipsPseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), [(callseq_start timm:$amt1, timm:$amt2)]>; def ADJCALLSTACKUP : MipsPseudo<(outs), (ins i32imm:$amt1, i32imm:$amt2), [(callseq_end timm:$amt1, timm:$amt2)]>; } let usesCustomInserter = 1 in { def ATOMIC_LOAD_ADD_I8 : Atomic2Ops; def ATOMIC_LOAD_ADD_I16 : Atomic2Ops; def ATOMIC_LOAD_ADD_I32 : Atomic2Ops; def ATOMIC_LOAD_SUB_I8 : Atomic2Ops; def ATOMIC_LOAD_SUB_I16 : Atomic2Ops; def ATOMIC_LOAD_SUB_I32 : Atomic2Ops; def ATOMIC_LOAD_AND_I8 : Atomic2Ops; def ATOMIC_LOAD_AND_I16 : Atomic2Ops; def ATOMIC_LOAD_AND_I32 : Atomic2Ops; def ATOMIC_LOAD_OR_I8 : Atomic2Ops; def ATOMIC_LOAD_OR_I16 : Atomic2Ops; def ATOMIC_LOAD_OR_I32 : Atomic2Ops; def ATOMIC_LOAD_XOR_I8 : Atomic2Ops; def ATOMIC_LOAD_XOR_I16 : Atomic2Ops; def ATOMIC_LOAD_XOR_I32 : Atomic2Ops; def ATOMIC_LOAD_NAND_I8 : Atomic2Ops; def ATOMIC_LOAD_NAND_I16 : Atomic2Ops; def ATOMIC_LOAD_NAND_I32 : Atomic2Ops; def ATOMIC_SWAP_I8 : Atomic2Ops; def ATOMIC_SWAP_I16 : Atomic2Ops; def ATOMIC_SWAP_I32 : Atomic2Ops; def ATOMIC_CMP_SWAP_I8 : AtomicCmpSwap; def ATOMIC_CMP_SWAP_I16 : AtomicCmpSwap; def ATOMIC_CMP_SWAP_I32 : AtomicCmpSwap; + } +def ATOMIC_LOAD_ADD_I8_POSTRA : Atomic2OpsSubwordPostRA; +def ATOMIC_LOAD_ADD_I16_POSTRA : Atomic2OpsSubwordPostRA; +def ATOMIC_LOAD_ADD_I32_POSTRA : Atomic2OpsPostRA; +def ATOMIC_LOAD_SUB_I8_POSTRA : Atomic2OpsSubwordPostRA; +def ATOMIC_LOAD_SUB_I16_POSTRA : Atomic2OpsSubwordPostRA; +def ATOMIC_LOAD_SUB_I32_POSTRA : Atomic2OpsPostRA; +def ATOMIC_LOAD_AND_I8_POSTRA : Atomic2OpsSubwordPostRA; +def ATOMIC_LOAD_AND_I16_POSTRA : Atomic2OpsSubwordPostRA; +def ATOMIC_LOAD_AND_I32_POSTRA : Atomic2OpsPostRA; +def ATOMIC_LOAD_OR_I8_POSTRA : Atomic2OpsSubwordPostRA; +def ATOMIC_LOAD_OR_I16_POSTRA : Atomic2OpsSubwordPostRA; +def ATOMIC_LOAD_OR_I32_POSTRA : Atomic2OpsPostRA; +def ATOMIC_LOAD_XOR_I8_POSTRA : Atomic2OpsSubwordPostRA; +def ATOMIC_LOAD_XOR_I16_POSTRA : Atomic2OpsSubwordPostRA; +def ATOMIC_LOAD_XOR_I32_POSTRA : Atomic2OpsPostRA; +def ATOMIC_LOAD_NAND_I8_POSTRA : Atomic2OpsSubwordPostRA; +def ATOMIC_LOAD_NAND_I16_POSTRA : Atomic2OpsSubwordPostRA; +def ATOMIC_LOAD_NAND_I32_POSTRA : Atomic2OpsPostRA; + +def ATOMIC_SWAP_I8_POSTRA : Atomic2OpsSubwordPostRA; +def ATOMIC_SWAP_I16_POSTRA : Atomic2OpsSubwordPostRA; +def ATOMIC_SWAP_I32_POSTRA : Atomic2OpsPostRA; + +def ATOMIC_CMP_SWAP_I8_POSTRA : AtomicCmpSwapSubwordPostRA; +def ATOMIC_CMP_SWAP_I16_POSTRA : AtomicCmpSwapSubwordPostRA; +def ATOMIC_CMP_SWAP_I32_POSTRA : AtomicCmpSwapPostRA; + /// Pseudo instructions for loading and storing accumulator registers. let isPseudo = 1, isCodeGenOnly = 1, hasNoSchedulingInfo = 1 in { def LOAD_ACC64 : Load<"", ACC64>; def STORE_ACC64 : Store<"", ACC64>; } // We need these two pseudo instructions to avoid offset calculation for long // branches. See the comment in file MipsLongBranch.cpp for detailed // explanation. // Expands to: lui $dst, %hi($tgt - $baltgt) def LONG_BRANCH_LUi : PseudoSE<(outs GPR32Opnd:$dst), (ins brtarget:$tgt, brtarget:$baltgt), []>; // Expands to: addiu $dst, $src, %lo($tgt - $baltgt) def LONG_BRANCH_ADDiu : PseudoSE<(outs GPR32Opnd:$dst), (ins GPR32Opnd:$src, brtarget:$tgt, brtarget:$baltgt), []>; //===----------------------------------------------------------------------===// // Instruction definition //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // MipsI Instructions //===----------------------------------------------------------------------===// /// Arithmetic Instructions (ALU Immediate) let AdditionalPredicates = [NotInMicroMips] in { def ADDiu : MMRel, StdMMR6Rel, ArithLogicI<"addiu", simm16_relaxed, GPR32Opnd, II_ADDIU, immSExt16, add>, ADDI_FM<0x9>, IsAsCheapAsAMove, ISA_MIPS1; def ANDi : MMRel, StdMMR6Rel, ArithLogicI<"andi", uimm16, GPR32Opnd, II_ANDI, immZExt16, and>, ADDI_FM<0xc>, ISA_MIPS1; def ORi : MMRel, StdMMR6Rel, ArithLogicI<"ori", uimm16, GPR32Opnd, II_ORI, immZExt16, or>, ADDI_FM<0xd>, ISA_MIPS1; def XORi : MMRel, StdMMR6Rel, ArithLogicI<"xori", uimm16, GPR32Opnd, II_XORI, immZExt16, xor>, ADDI_FM<0xe>, ISA_MIPS1; def ADDi : MMRel, ArithLogicI<"addi", simm16_relaxed, GPR32Opnd, II_ADDI>, ADDI_FM<0x8>, ISA_MIPS1_NOT_32R6_64R6; def SLTi : MMRel, SetCC_I<"slti", setlt, simm16, immSExt16, GPR32Opnd>, SLTI_FM<0xa>, ISA_MIPS1; def SLTiu : MMRel, SetCC_I<"sltiu", setult, simm16, immSExt16, GPR32Opnd>, SLTI_FM<0xb>, ISA_MIPS1; def LUi : MMRel, LoadUpper<"lui", GPR32Opnd, uimm16_relaxed>, LUI_FM, ISA_MIPS1; /// Arithmetic Instructions (3-Operand, R-Type) def ADDu : MMRel, StdMMR6Rel, ArithLogicR<"addu", GPR32Opnd, 1, II_ADDU, add>, ADD_FM<0, 0x21>, ISA_MIPS1; def SUBu : MMRel, StdMMR6Rel, ArithLogicR<"subu", GPR32Opnd, 0, II_SUBU, sub>, ADD_FM<0, 0x23>, ISA_MIPS1; let Defs = [HI0, LO0] in def MUL : MMRel, ArithLogicR<"mul", GPR32Opnd, 1, II_MUL, mul>, ADD_FM<0x1c, 2>, ISA_MIPS32_NOT_32R6_64R6; def ADD : MMRel, StdMMR6Rel, ArithLogicR<"add", GPR32Opnd, 1, II_ADD>, ADD_FM<0, 0x20>, ISA_MIPS1; def SUB : MMRel, StdMMR6Rel, ArithLogicR<"sub", GPR32Opnd, 0, II_SUB>, ADD_FM<0, 0x22>, ISA_MIPS1; def SLT : MMRel, SetCC_R<"slt", setlt, GPR32Opnd>, ADD_FM<0, 0x2a>, ISA_MIPS1; def SLTu : MMRel, SetCC_R<"sltu", setult, GPR32Opnd>, ADD_FM<0, 0x2b>, ISA_MIPS1; def AND : MMRel, StdMMR6Rel, ArithLogicR<"and", GPR32Opnd, 1, II_AND, and>, ADD_FM<0, 0x24>, ISA_MIPS1; def OR : MMRel, StdMMR6Rel, ArithLogicR<"or", GPR32Opnd, 1, II_OR, or>, ADD_FM<0, 0x25>, ISA_MIPS1; def XOR : MMRel, StdMMR6Rel, ArithLogicR<"xor", GPR32Opnd, 1, II_XOR, xor>, ADD_FM<0, 0x26>, ISA_MIPS1; def NOR : MMRel, StdMMR6Rel, LogicNOR<"nor", GPR32Opnd>, ADD_FM<0, 0x27>, ISA_MIPS1; } let AdditionalPredicates = [NotInMicroMips] in { /// Shift Instructions def SLL : MMRel, shift_rotate_imm<"sll", uimm5, GPR32Opnd, II_SLL, shl, immZExt5>, SRA_FM<0, 0>, ISA_MIPS1; def SRL : MMRel, shift_rotate_imm<"srl", uimm5, GPR32Opnd, II_SRL, srl, immZExt5>, SRA_FM<2, 0>, ISA_MIPS1; def SRA : MMRel, shift_rotate_imm<"sra", uimm5, GPR32Opnd, II_SRA, sra, immZExt5>, SRA_FM<3, 0>, ISA_MIPS1; def SLLV : MMRel, shift_rotate_reg<"sllv", GPR32Opnd, II_SLLV, shl>, SRLV_FM<4, 0>, ISA_MIPS1; def SRLV : MMRel, shift_rotate_reg<"srlv", GPR32Opnd, II_SRLV, srl>, SRLV_FM<6, 0>, ISA_MIPS1; def SRAV : MMRel, shift_rotate_reg<"srav", GPR32Opnd, II_SRAV, sra>, SRLV_FM<7, 0>, ISA_MIPS1; // Rotate Instructions def ROTR : MMRel, shift_rotate_imm<"rotr", uimm5, GPR32Opnd, II_ROTR, rotr, immZExt5>, SRA_FM<2, 1>, ISA_MIPS32R2; def ROTRV : MMRel, shift_rotate_reg<"rotrv", GPR32Opnd, II_ROTRV, rotr>, SRLV_FM<6, 1>, ISA_MIPS32R2; } /// Load and Store Instructions /// aligned let AdditionalPredicates = [NotInMicroMips] in { def LB : LoadMemory<"lb", GPR32Opnd, mem_simmptr, sextloadi8, II_LB>, MMRel, LW_FM<0x20>, ISA_MIPS1; def LBu : LoadMemory<"lbu", GPR32Opnd, mem_simmptr, zextloadi8, II_LBU, addrDefault>, MMRel, LW_FM<0x24>, ISA_MIPS1; def LH : LoadMemory<"lh", GPR32Opnd, mem_simmptr, sextloadi16, II_LH, addrDefault>, MMRel, LW_FM<0x21>, ISA_MIPS1; def LHu : LoadMemory<"lhu", GPR32Opnd, mem_simmptr, zextloadi16, II_LHU>, MMRel, LW_FM<0x25>, ISA_MIPS1; def LW : StdMMR6Rel, Load<"lw", GPR32Opnd, load, II_LW, addrDefault>, MMRel, LW_FM<0x23>, ISA_MIPS1; def SB : StdMMR6Rel, Store<"sb", GPR32Opnd, truncstorei8, II_SB>, MMRel, LW_FM<0x28>, ISA_MIPS1; def SH : Store<"sh", GPR32Opnd, truncstorei16, II_SH>, MMRel, LW_FM<0x29>, ISA_MIPS1; def SW : Store<"sw", GPR32Opnd, store, II_SW>, MMRel, LW_FM<0x2b>, ISA_MIPS1; } /// load/store left/right let AdditionalPredicates = [NotInMicroMips] in { def LWL : MMRel, LoadLeftRight<"lwl", MipsLWL, GPR32Opnd, II_LWL>, LW_FM<0x22>, ISA_MIPS1_NOT_32R6_64R6; def LWR : MMRel, LoadLeftRight<"lwr", MipsLWR, GPR32Opnd, II_LWR>, LW_FM<0x26>, ISA_MIPS1_NOT_32R6_64R6; def SWL : MMRel, StoreLeftRight<"swl", MipsSWL, GPR32Opnd, II_SWL>, LW_FM<0x2a>, ISA_MIPS1_NOT_32R6_64R6; def SWR : MMRel, StoreLeftRight<"swr", MipsSWR, GPR32Opnd, II_SWR>, LW_FM<0x2e>, ISA_MIPS1_NOT_32R6_64R6; // COP2 Memory Instructions def LWC2 : StdMMR6Rel, LW_FT2<"lwc2", COP2Opnd, II_LWC2, load>, LW_FM<0x32>, ISA_MIPS1_NOT_32R6_64R6; def SWC2 : StdMMR6Rel, SW_FT2<"swc2", COP2Opnd, II_SWC2, store>, LW_FM<0x3a>, ISA_MIPS1_NOT_32R6_64R6; def LDC2 : StdMMR6Rel, LW_FT2<"ldc2", COP2Opnd, II_LDC2, load>, LW_FM<0x36>, ISA_MIPS2_NOT_32R6_64R6; def SDC2 : StdMMR6Rel, SW_FT2<"sdc2", COP2Opnd, II_SDC2, store>, LW_FM<0x3e>, ISA_MIPS2_NOT_32R6_64R6; // COP3 Memory Instructions let DecoderNamespace = "COP3_" in { def LWC3 : LW_FT3<"lwc3", COP3Opnd, II_LWC3, load>, LW_FM<0x33>, ISA_MIPS1_NOT_32R6_64R6, NOT_ASE_CNMIPS; def SWC3 : SW_FT3<"swc3", COP3Opnd, II_SWC3, store>, LW_FM<0x3b>, ISA_MIPS1_NOT_32R6_64R6, NOT_ASE_CNMIPS; def LDC3 : LW_FT3<"ldc3", COP3Opnd, II_LDC3, load>, LW_FM<0x37>, ISA_MIPS2, NOT_ASE_CNMIPS; def SDC3 : SW_FT3<"sdc3", COP3Opnd, II_SDC3, store>, LW_FM<0x3f>, ISA_MIPS2, NOT_ASE_CNMIPS; } def SYNC : MMRel, StdMMR6Rel, SYNC_FT<"sync">, SYNC_FM, ISA_MIPS2; def SYNCI : MMRel, StdMMR6Rel, SYNCI_FT<"synci", mem_simm16>, SYNCI_FM, ISA_MIPS32R2; } let AdditionalPredicates = [NotInMicroMips] in { def TEQ : MMRel, TEQ_FT<"teq", GPR32Opnd, uimm10, II_TEQ>, TEQ_FM<0x34>, ISA_MIPS2; def TGE : MMRel, TEQ_FT<"tge", GPR32Opnd, uimm10, II_TGE>, TEQ_FM<0x30>, ISA_MIPS2; def TGEU : MMRel, TEQ_FT<"tgeu", GPR32Opnd, uimm10, II_TGEU>, TEQ_FM<0x31>, ISA_MIPS2; def TLT : MMRel, TEQ_FT<"tlt", GPR32Opnd, uimm10, II_TLT>, TEQ_FM<0x32>, ISA_MIPS2; def TLTU : MMRel, TEQ_FT<"tltu", GPR32Opnd, uimm10, II_TLTU>, TEQ_FM<0x33>, ISA_MIPS2; def TNE : MMRel, TEQ_FT<"tne", GPR32Opnd, uimm10, II_TNE>, TEQ_FM<0x36>, ISA_MIPS2; def TEQI : MMRel, TEQI_FT<"teqi", GPR32Opnd, II_TEQI>, TEQI_FM<0xc>, ISA_MIPS2_NOT_32R6_64R6; def TGEI : MMRel, TEQI_FT<"tgei", GPR32Opnd, II_TGEI>, TEQI_FM<0x8>, ISA_MIPS2_NOT_32R6_64R6; def TGEIU : MMRel, TEQI_FT<"tgeiu", GPR32Opnd, II_TGEIU>, TEQI_FM<0x9>, ISA_MIPS2_NOT_32R6_64R6; def TLTI : MMRel, TEQI_FT<"tlti", GPR32Opnd, II_TLTI>, TEQI_FM<0xa>, ISA_MIPS2_NOT_32R6_64R6; def TTLTIU : MMRel, TEQI_FT<"tltiu", GPR32Opnd, II_TTLTIU>, TEQI_FM<0xb>, ISA_MIPS2_NOT_32R6_64R6; def TNEI : MMRel, TEQI_FT<"tnei", GPR32Opnd, II_TNEI>, TEQI_FM<0xe>, ISA_MIPS2_NOT_32R6_64R6; } let AdditionalPredicates = [NotInMicroMips] in { def BREAK : MMRel, StdMMR6Rel, BRK_FT<"break">, BRK_FM<0xd>, ISA_MIPS1; def SYSCALL : MMRel, SYS_FT<"syscall", uimm20, II_SYSCALL>, SYS_FM<0xc>, ISA_MIPS1; def TRAP : TrapBase, ISA_MIPS1; def SDBBP : MMRel, SYS_FT<"sdbbp", uimm20, II_SDBBP>, SDBBP_FM, ISA_MIPS32_NOT_32R6_64R6; def ERET : MMRel, ER_FT<"eret", II_ERET>, ER_FM<0x18, 0x0>, INSN_MIPS3_32; def ERETNC : MMRel, ER_FT<"eretnc", II_ERETNC>, ER_FM<0x18, 0x1>, ISA_MIPS32R5; def DERET : MMRel, ER_FT<"deret", II_DERET>, ER_FM<0x1f, 0x0>, ISA_MIPS32; def EI : MMRel, StdMMR6Rel, DEI_FT<"ei", GPR32Opnd, II_EI>, EI_FM<1>, ISA_MIPS32R2; def DI : MMRel, StdMMR6Rel, DEI_FT<"di", GPR32Opnd, II_DI>, EI_FM<0>, ISA_MIPS32R2; def WAIT : MMRel, StdMMR6Rel, WAIT_FT<"wait">, WAIT_FM, INSN_MIPS3_32; } let AdditionalPredicates = [NotInMicroMips] in { /// Load-linked, Store-conditional def LL : LLBase<"ll", GPR32Opnd>, LW_FM<0x30>, PTR_32, ISA_MIPS2_NOT_32R6_64R6; def SC : SCBase<"sc", GPR32Opnd>, LW_FM<0x38>, PTR_32, ISA_MIPS2_NOT_32R6_64R6; } /// Jump and Branch Instructions let AdditionalPredicates = [NotInMicroMips, RelocNotPIC] in def J : MMRel, JumpFJ, FJ<2>, IsBranch, ISA_MIPS1; let AdditionalPredicates = [NotInMicroMips] in { def JR : MMRel, IndirectBranch<"jr", GPR32Opnd>, MTLO_FM<8>, ISA_MIPS1_NOT_32R6_64R6; def BEQ : MMRel, CBranch<"beq", brtarget, seteq, GPR32Opnd>, BEQ_FM<4>, ISA_MIPS1; def BEQL : MMRel, CBranchLikely<"beql", brtarget, GPR32Opnd>, BEQ_FM<20>, ISA_MIPS2_NOT_32R6_64R6; def BNE : MMRel, CBranch<"bne", brtarget, setne, GPR32Opnd>, BEQ_FM<5>, ISA_MIPS1; def BNEL : MMRel, CBranchLikely<"bnel", brtarget, GPR32Opnd>, BEQ_FM<21>, ISA_MIPS2_NOT_32R6_64R6; def BGEZ : MMRel, CBranchZero<"bgez", brtarget, setge, GPR32Opnd>, BGEZ_FM<1, 1>, ISA_MIPS1; def BGEZL : MMRel, CBranchZeroLikely<"bgezl", brtarget, GPR32Opnd>, BGEZ_FM<1, 3>, ISA_MIPS2_NOT_32R6_64R6; def BGTZ : MMRel, CBranchZero<"bgtz", brtarget, setgt, GPR32Opnd>, BGEZ_FM<7, 0>, ISA_MIPS1; def BGTZL : MMRel, CBranchZeroLikely<"bgtzl", brtarget, GPR32Opnd>, BGEZ_FM<23, 0>, ISA_MIPS2_NOT_32R6_64R6; def BLEZ : MMRel, CBranchZero<"blez", brtarget, setle, GPR32Opnd>, BGEZ_FM<6, 0>, ISA_MIPS1; def BLEZL : MMRel, CBranchZeroLikely<"blezl", brtarget, GPR32Opnd>, BGEZ_FM<22, 0>, ISA_MIPS2_NOT_32R6_64R6; def BLTZ : MMRel, CBranchZero<"bltz", brtarget, setlt, GPR32Opnd>, BGEZ_FM<1, 0>, ISA_MIPS1; def BLTZL : MMRel, CBranchZeroLikely<"bltzl", brtarget, GPR32Opnd>, BGEZ_FM<1, 2>, ISA_MIPS2_NOT_32R6_64R6; def B : UncondBranch, ISA_MIPS1; def JAL : MMRel, JumpLink<"jal", calltarget>, FJ<3>, ISA_MIPS1; } let AdditionalPredicates = [NotInMicroMips, NoIndirectJumpGuards] in { def JALR : JumpLinkReg<"jalr", GPR32Opnd>, JALR_FM, ISA_MIPS1; def JALRPseudo : JumpLinkRegPseudo, ISA_MIPS1; } let AdditionalPredicates = [NotInMicroMips] in { def JALX : MMRel, JumpLink<"jalx", calltarget>, FJ<0x1D>, ISA_MIPS32_NOT_32R6_64R6; def BGEZAL : MMRel, BGEZAL_FT<"bgezal", brtarget, GPR32Opnd>, BGEZAL_FM<0x11>, ISA_MIPS1_NOT_32R6_64R6; def BGEZALL : MMRel, BGEZAL_FT<"bgezall", brtarget, GPR32Opnd>, BGEZAL_FM<0x13>, ISA_MIPS2_NOT_32R6_64R6; def BLTZAL : MMRel, BGEZAL_FT<"bltzal", brtarget, GPR32Opnd>, BGEZAL_FM<0x10>, ISA_MIPS1_NOT_32R6_64R6; def BLTZALL : MMRel, BGEZAL_FT<"bltzall", brtarget, GPR32Opnd>, BGEZAL_FM<0x12>, ISA_MIPS2_NOT_32R6_64R6; def BAL_BR : BAL_BR_Pseudo, ISA_MIPS1; } let AdditionalPredicates = [NotInMips16Mode, NotInMicroMips] in { def TAILCALL : TailCall, ISA_MIPS1; } let AdditionalPredicates = [NotInMips16Mode, NotInMicroMips, NoIndirectJumpGuards] in def TAILCALLREG : TailCallReg, ISA_MIPS1_NOT_32R6_64R6; // Indirect branches are matched as PseudoIndirectBranch/PseudoIndirectBranch64 // then are expanded to JR, JR64, JALR, or JALR64 depending on the ISA. class PseudoIndirectBranchBase : MipsPseudo<(outs), (ins RO:$rs), [(brind RO:$rs)], II_IndirectBranchPseudo>, PseudoInstExpansion<(JumpInst RO:$rs)> { let isTerminator=1; let isBarrier=1; let hasDelaySlot = 1; let isBranch = 1; let isIndirectBranch = 1; bit isCTI = 1; } let AdditionalPredicates = [NotInMips16Mode, NotInMicroMips, NoIndirectJumpGuards] in def PseudoIndirectBranch : PseudoIndirectBranchBase, ISA_MIPS1_NOT_32R6_64R6; // Return instructions are matched as a RetRA instruction, then are expanded // into PseudoReturn/PseudoReturn64 after register allocation. Finally, // MipsAsmPrinter expands this into JR, JR64, JALR, or JALR64 depending on the // ISA. class PseudoReturnBase : MipsPseudo<(outs), (ins RO:$rs), [], II_ReturnPseudo> { let isTerminator = 1; let isBarrier = 1; let hasDelaySlot = 1; let isReturn = 1; let isCodeGenOnly = 1; let hasCtrlDep = 1; let hasExtraSrcRegAllocReq = 1; bit isCTI = 1; } def PseudoReturn : PseudoReturnBase; // Exception handling related node and instructions. // The conversion sequence is: // ISD::EH_RETURN -> MipsISD::EH_RETURN -> // MIPSeh_return -> (stack change + indirect branch) // // MIPSeh_return takes the place of regular return instruction // but takes two arguments (V1, V0) which are used for storing // the offset and return address respectively. def SDT_MipsEHRET : SDTypeProfile<0, 2, [SDTCisInt<0>, SDTCisPtrTy<1>]>; def MIPSehret : SDNode<"MipsISD::EH_RETURN", SDT_MipsEHRET, [SDNPHasChain, SDNPOptInGlue, SDNPVariadic]>; let Uses = [V0, V1], isTerminator = 1, isReturn = 1, isBarrier = 1, isCTI = 1 in { def MIPSeh_return32 : MipsPseudo<(outs), (ins GPR32:$spoff, GPR32:$dst), [(MIPSehret GPR32:$spoff, GPR32:$dst)]>; def MIPSeh_return64 : MipsPseudo<(outs), (ins GPR64:$spoff, GPR64:$dst), [(MIPSehret GPR64:$spoff, GPR64:$dst)]>; } /// Multiply and Divide Instructions. let AdditionalPredicates = [NotInMicroMips] in { def MULT : MMRel, Mult<"mult", II_MULT, GPR32Opnd, [HI0, LO0]>, MULT_FM<0, 0x18>, ISA_MIPS1_NOT_32R6_64R6; def MULTu : MMRel, Mult<"multu", II_MULTU, GPR32Opnd, [HI0, LO0]>, MULT_FM<0, 0x19>, ISA_MIPS1_NOT_32R6_64R6; def SDIV : MMRel, Div<"div", II_DIV, GPR32Opnd, [HI0, LO0]>, MULT_FM<0, 0x1a>, ISA_MIPS1_NOT_32R6_64R6; def UDIV : MMRel, Div<"divu", II_DIVU, GPR32Opnd, [HI0, LO0]>, MULT_FM<0, 0x1b>, ISA_MIPS1_NOT_32R6_64R6; def MTHI : MMRel, MoveToLOHI<"mthi", GPR32Opnd, [HI0]>, MTLO_FM<0x11>, ISA_MIPS1_NOT_32R6_64R6; def MTLO : MMRel, MoveToLOHI<"mtlo", GPR32Opnd, [LO0]>, MTLO_FM<0x13>, ISA_MIPS1_NOT_32R6_64R6; def MFHI : MMRel, MoveFromLOHI<"mfhi", GPR32Opnd, AC0>, MFLO_FM<0x10>, ISA_MIPS1_NOT_32R6_64R6; def MFLO : MMRel, MoveFromLOHI<"mflo", GPR32Opnd, AC0>, MFLO_FM<0x12>, ISA_MIPS1_NOT_32R6_64R6; /// Sign Ext In Register Instructions. def SEB : MMRel, StdMMR6Rel, SignExtInReg<"seb", i8, GPR32Opnd, II_SEB>, SEB_FM<0x10, 0x20>, ISA_MIPS32R2; def SEH : MMRel, StdMMR6Rel, SignExtInReg<"seh", i16, GPR32Opnd, II_SEH>, SEB_FM<0x18, 0x20>, ISA_MIPS32R2; /// Count Leading def CLZ : MMRel, CountLeading0<"clz", GPR32Opnd, II_CLZ>, CLO_FM<0x20>, ISA_MIPS32_NOT_32R6_64R6; def CLO : MMRel, CountLeading1<"clo", GPR32Opnd, II_CLO>, CLO_FM<0x21>, ISA_MIPS32_NOT_32R6_64R6; /// Word Swap Bytes Within Halfwords def WSBH : MMRel, SubwordSwap<"wsbh", GPR32Opnd, II_WSBH>, SEB_FM<2, 0x20>, ISA_MIPS32R2; /// No operation. def NOP : PseudoSE<(outs), (ins), []>, PseudoInstExpansion<(SLL ZERO, ZERO, 0)>, ISA_MIPS1; // FrameIndexes are legalized when they are operands from load/store // instructions. The same not happens for stack address copies, so an // add op with mem ComplexPattern is used and the stack address copy // can be matched. It's similar to Sparc LEA_ADDRi let AdditionalPredicates = [NotInMicroMips] in def LEA_ADDiu : MMRel, EffectiveAddress<"addiu", GPR32Opnd>, LW_FM<9>, ISA_MIPS1; // MADD*/MSUB* def MADD : MMRel, MArithR<"madd", II_MADD, 1>, MULT_FM<0x1c, 0>, ISA_MIPS32_NOT_32R6_64R6; def MADDU : MMRel, MArithR<"maddu", II_MADDU, 1>, MULT_FM<0x1c, 1>, ISA_MIPS32_NOT_32R6_64R6; def MSUB : MMRel, MArithR<"msub", II_MSUB>, MULT_FM<0x1c, 4>, ISA_MIPS32_NOT_32R6_64R6; def MSUBU : MMRel, MArithR<"msubu", II_MSUBU>, MULT_FM<0x1c, 5>, ISA_MIPS32_NOT_32R6_64R6; } let AdditionalPredicates = [NotDSP] in { def PseudoMULT : MultDivPseudo, ISA_MIPS1_NOT_32R6_64R6; def PseudoMULTu : MultDivPseudo, ISA_MIPS1_NOT_32R6_64R6; def PseudoMFHI : PseudoMFLOHI, ISA_MIPS1_NOT_32R6_64R6; def PseudoMFLO : PseudoMFLOHI, ISA_MIPS1_NOT_32R6_64R6; def PseudoMTLOHI : PseudoMTLOHI, ISA_MIPS1_NOT_32R6_64R6; def PseudoMADD : MAddSubPseudo, ISA_MIPS32_NOT_32R6_64R6; def PseudoMADDU : MAddSubPseudo, ISA_MIPS32_NOT_32R6_64R6; def PseudoMSUB : MAddSubPseudo, ISA_MIPS32_NOT_32R6_64R6; def PseudoMSUBU : MAddSubPseudo, ISA_MIPS32_NOT_32R6_64R6; } let AdditionalPredicates = [NotInMicroMips] in { def PseudoSDIV : MultDivPseudo, ISA_MIPS1_NOT_32R6_64R6; def PseudoUDIV : MultDivPseudo, ISA_MIPS1_NOT_32R6_64R6; def RDHWR : MMRel, ReadHardware, RDHWR_FM, ISA_MIPS1; // TODO: Add '0 < pos+size <= 32' constraint check to ext instruction def EXT : MMRel, StdMMR6Rel, ExtBase<"ext", GPR32Opnd, uimm5, uimm5_plus1, immZExt5, immZExt5Plus1, MipsExt>, EXT_FM<0>, ISA_MIPS32R2; def INS : MMRel, StdMMR6Rel, InsBase<"ins", GPR32Opnd, uimm5, uimm5_inssize_plus1, immZExt5, immZExt5Plus1>, EXT_FM<4>, ISA_MIPS32R2; } /// Move Control Registers From/To CPU Registers let AdditionalPredicates = [NotInMicroMips] in { def MTC0 : MTC3OP<"mtc0", COP0Opnd, GPR32Opnd, II_MTC0>, MFC3OP_FM<0x10, 4, 0>, ISA_MIPS1; def MFC0 : MFC3OP<"mfc0", GPR32Opnd, COP0Opnd, II_MFC0>, MFC3OP_FM<0x10, 0, 0>, ISA_MIPS1; def MFC2 : MFC3OP<"mfc2", GPR32Opnd, COP2Opnd, II_MFC2>, MFC3OP_FM<0x12, 0, 0>, ISA_MIPS1; def MTC2 : MTC3OP<"mtc2", COP2Opnd, GPR32Opnd, II_MTC2>, MFC3OP_FM<0x12, 4, 0>, ISA_MIPS1; } class Barrier : InstSE<(outs), (ins), asmstr, [], itin, FrmOther, asmstr>; let AdditionalPredicates = [NotInMicroMips] in { def SSNOP : MMRel, StdMMR6Rel, Barrier<"ssnop", II_SSNOP>, BARRIER_FM<1>, ISA_MIPS1; def EHB : MMRel, Barrier<"ehb", II_EHB>, BARRIER_FM<3>, ISA_MIPS1; let isCTI = 1 in def PAUSE : MMRel, StdMMR6Rel, Barrier<"pause", II_PAUSE>, BARRIER_FM<5>, ISA_MIPS32R2; } // JR_HB and JALR_HB are defined here using the new style naming // scheme because some of this code is shared with Mips32r6InstrInfo.td // and because of that it doesn't follow the naming convention of the // rest of the file. To avoid a mixture of old vs new style, the new // style was chosen. class JR_HB_DESC_BASE { dag OutOperandList = (outs); dag InOperandList = (ins GPROpnd:$rs); string AsmString = !strconcat(instr_asm, "\t$rs"); list Pattern = []; } class JALR_HB_DESC_BASE { dag OutOperandList = (outs GPROpnd:$rd); dag InOperandList = (ins GPROpnd:$rs); string AsmString = !strconcat(instr_asm, "\t$rd, $rs"); list Pattern = []; } class JR_HB_DESC : InstSE<(outs), (ins), "", [], II_JR_HB, FrmJ>, JR_HB_DESC_BASE<"jr.hb", RO> { let isBranch=1; let isIndirectBranch=1; let hasDelaySlot=1; let isTerminator=1; let isBarrier=1; bit isCTI = 1; } class JALR_HB_DESC : InstSE<(outs), (ins), "", [], II_JALR_HB, FrmJ>, JALR_HB_DESC_BASE<"jalr.hb", RO> { let isIndirectBranch=1; let hasDelaySlot=1; bit isCTI = 1; } class JR_HB_ENC : JR_HB_FM<8>; class JALR_HB_ENC : JALR_HB_FM<9>; def JR_HB : JR_HB_DESC, JR_HB_ENC, ISA_MIPS32R2_NOT_32R6_64R6; def JALR_HB : JALR_HB_DESC, JALR_HB_ENC, ISA_MIPS32; let AdditionalPredicates = [NotInMicroMips, UseIndirectJumpsHazard] in def JALRHBPseudo : JumpLinkRegPseudo; let AdditionalPredicates = [NotInMips16Mode, NotInMicroMips, UseIndirectJumpsHazard] in { def TAILCALLREGHB : TailCallReg, ISA_MIPS32_NOT_32R6_64R6; def PseudoIndirectHazardBranch : PseudoIndirectBranchBase, ISA_MIPS32R2_NOT_32R6_64R6; } class TLB : InstSE<(outs), (ins), asmstr, [], itin, FrmOther, asmstr>; let AdditionalPredicates = [NotInMicroMips] in { def TLBP : MMRel, TLB<"tlbp", II_TLBP>, COP0_TLB_FM<0x08>, ISA_MIPS1; def TLBR : MMRel, TLB<"tlbr", II_TLBR>, COP0_TLB_FM<0x01>, ISA_MIPS1; def TLBWI : MMRel, TLB<"tlbwi", II_TLBWI>, COP0_TLB_FM<0x02>, ISA_MIPS1; def TLBWR : MMRel, TLB<"tlbwr", II_TLBWR>, COP0_TLB_FM<0x06>, ISA_MIPS1; } class CacheOp : InstSE<(outs), (ins MemOpnd:$addr, uimm5:$hint), !strconcat(instr_asm, "\t$hint, $addr"), [], itin, FrmOther, instr_asm> { let DecoderMethod = "DecodeCacheOp"; } let AdditionalPredicates = [NotInMicroMips] in { def CACHE : MMRel, CacheOp<"cache", mem, II_CACHE>, CACHEOP_FM<0b101111>, INSN_MIPS3_32_NOT_32R6_64R6; def PREF : MMRel, CacheOp<"pref", mem, II_PREF>, CACHEOP_FM<0b110011>, INSN_MIPS3_32_NOT_32R6_64R6; } // FIXME: We are missing the prefx instruction. def ROL : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rd), "rol\t$rs, $rt, $rd">; def ROLImm : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, simm16:$imm), "rol\t$rs, $rt, $imm">; def : MipsInstAlias<"rol $rd, $rs", (ROL GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rs), 0>; def : MipsInstAlias<"rol $rd, $imm", (ROLImm GPR32Opnd:$rd, GPR32Opnd:$rd, simm16:$imm), 0>; def ROR : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rd), "ror\t$rs, $rt, $rd">; def RORImm : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, simm16:$imm), "ror\t$rs, $rt, $imm">; def : MipsInstAlias<"ror $rd, $rs", (ROR GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rs), 0>; def : MipsInstAlias<"ror $rd, $imm", (RORImm GPR32Opnd:$rd, GPR32Opnd:$rd, simm16:$imm), 0>; def DROL : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rd), "drol\t$rs, $rt, $rd">, ISA_MIPS64; def DROLImm : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, simm16:$imm), "drol\t$rs, $rt, $imm">, ISA_MIPS64; def : MipsInstAlias<"drol $rd, $rs", (DROL GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rs), 0>, ISA_MIPS64; def : MipsInstAlias<"drol $rd, $imm", (DROLImm GPR32Opnd:$rd, GPR32Opnd:$rd, simm16:$imm), 0>, ISA_MIPS64; def DROR : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rd), "dror\t$rs, $rt, $rd">, ISA_MIPS64; def DRORImm : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, simm16:$imm), "dror\t$rs, $rt, $imm">, ISA_MIPS64; def : MipsInstAlias<"dror $rd, $rs", (DROR GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rs), 0>, ISA_MIPS64; def : MipsInstAlias<"dror $rd, $imm", (DRORImm GPR32Opnd:$rd, GPR32Opnd:$rd, simm16:$imm), 0>, ISA_MIPS64; def ABSMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs), "abs\t$rd, $rs">; def SEQMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, GPR32Opnd:$rt), "seq $rd, $rs, $rt">, NOT_ASE_CNMIPS; def : MipsInstAlias<"seq $rd, $rs", (SEQMacro GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rs), 0>, NOT_ASE_CNMIPS; def SEQIMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, simm32_relaxed:$imm), "seq $rd, $rs, $imm">, NOT_ASE_CNMIPS; def : MipsInstAlias<"seq $rd, $imm", (SEQIMacro GPR32Opnd:$rd, GPR32Opnd:$rd, simm32:$imm), 0>, NOT_ASE_CNMIPS; def MULImmMacro : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rd, GPR32Opnd:$rs, simm32_relaxed:$imm), "mul\t$rd, $rs, $imm">, ISA_MIPS1_NOT_32R6_64R6; def MULOMacro : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rd, GPR32Opnd:$rs, GPR32Opnd:$rt), "mulo\t$rd, $rs, $rt">, ISA_MIPS1_NOT_32R6_64R6; def MULOUMacro : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rd, GPR32Opnd:$rs, GPR32Opnd:$rt), "mulou\t$rd, $rs, $rt">, ISA_MIPS1_NOT_32R6_64R6; // Virtualization ASE class HYPCALL_FT : InstSE<(outs), (ins uimm10:$code_), !strconcat(opstr, "\t$code_"), [], II_HYPCALL, FrmOther, opstr> { let BaseOpcode = opstr; } let AdditionalPredicates = [NotInMicroMips] in { def MFGC0 : MMRel, MFC3OP<"mfgc0", GPR32Opnd, COP0Opnd, II_MFGC0>, MFC3OP_FM<0x10, 3, 0>, ISA_MIPS32R5, ASE_VIRT; def MTGC0 : MMRel, MTC3OP<"mtgc0", COP0Opnd, GPR32Opnd, II_MTGC0>, MFC3OP_FM<0x10, 3, 2>, ISA_MIPS32R5, ASE_VIRT; def MFHGC0 : MMRel, MFC3OP<"mfhgc0", GPR32Opnd, COP0Opnd, II_MFHGC0>, MFC3OP_FM<0x10, 3, 4>, ISA_MIPS32R5, ASE_VIRT; def MTHGC0 : MMRel, MTC3OP<"mthgc0", COP0Opnd, GPR32Opnd, II_MTHGC0>, MFC3OP_FM<0x10, 3, 6>, ISA_MIPS32R5, ASE_VIRT; def TLBGINV : MMRel, TLB<"tlbginv", II_TLBGINV>, COP0_TLB_FM<0b001011>, ISA_MIPS32R5, ASE_VIRT; def TLBGINVF : MMRel, TLB<"tlbginvf", II_TLBGINVF>, COP0_TLB_FM<0b001100>, ISA_MIPS32R5, ASE_VIRT; def TLBGP : MMRel, TLB<"tlbgp", II_TLBGP>, COP0_TLB_FM<0b010000>, ISA_MIPS32R5, ASE_VIRT; def TLBGR : MMRel, TLB<"tlbgr", II_TLBGR>, COP0_TLB_FM<0b001001>, ISA_MIPS32R5, ASE_VIRT; def TLBGWI : MMRel, TLB<"tlbgwi", II_TLBGWI>, COP0_TLB_FM<0b001010>, ISA_MIPS32R5, ASE_VIRT; def TLBGWR : MMRel, TLB<"tlbgwr", II_TLBGWR>, COP0_TLB_FM<0b001110>, ISA_MIPS32R5, ASE_VIRT; def HYPCALL : MMRel, HYPCALL_FT<"hypcall">, HYPCALL_FM<0b101000>, ISA_MIPS32R5, ASE_VIRT; } //===----------------------------------------------------------------------===// // Instruction aliases //===----------------------------------------------------------------------===// multiclass OneOrTwoOperandMacroImmediateAlias { def : MipsInstAlias; def : MipsInstAlias; } let AdditionalPredicates = [NotInMicroMips] in { def : MipsInstAlias<"move $dst, $src", (OR GPR32Opnd:$dst, GPR32Opnd:$src, ZERO), 1>, GPR_32, ISA_MIPS1; def : MipsInstAlias<"move $dst, $src", (ADDu GPR32Opnd:$dst, GPR32Opnd:$src, ZERO), 1>, GPR_32, ISA_MIPS1; def : MipsInstAlias<"bal $offset", (BGEZAL ZERO, brtarget:$offset), 1>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"j $rs", (JR GPR32Opnd:$rs), 0>, ISA_MIPS1; def : MipsInstAlias<"jalr $rs", (JALR RA, GPR32Opnd:$rs), 0>; def : MipsInstAlias<"jalr.hb $rs", (JALR_HB RA, GPR32Opnd:$rs), 1>, ISA_MIPS32; def : MipsInstAlias<"neg $rt, $rs", (SUB GPR32Opnd:$rt, ZERO, GPR32Opnd:$rs), 1>, ISA_MIPS1; def : MipsInstAlias<"neg $rt", (SUB GPR32Opnd:$rt, ZERO, GPR32Opnd:$rt), 1>, ISA_MIPS1; def : MipsInstAlias<"negu $rt, $rs", (SUBu GPR32Opnd:$rt, ZERO, GPR32Opnd:$rs), 1>, ISA_MIPS1; def : MipsInstAlias<"negu $rt", (SUBu GPR32Opnd:$rt, ZERO, GPR32Opnd:$rt), 1>, ISA_MIPS1; def : MipsInstAlias< "sgt $rd, $rs, $rt", (SLT GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS1; def : MipsInstAlias< "sgt $rs, $rt", (SLT GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS1; def : MipsInstAlias< "sgtu $rd, $rs, $rt", (SLTu GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS1; def : MipsInstAlias< "sgtu $$rs, $rt", (SLTu GPR32Opnd:$rs, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS1; def : MipsInstAlias< "not $rt, $rs", (NOR GPR32Opnd:$rt, GPR32Opnd:$rs, ZERO), 0>, ISA_MIPS1; def : MipsInstAlias< "not $rt", (NOR GPR32Opnd:$rt, GPR32Opnd:$rt, ZERO), 0>, ISA_MIPS1; def : MipsInstAlias<"nop", (SLL ZERO, ZERO, 0), 1>, ISA_MIPS1; defm : OneOrTwoOperandMacroImmediateAlias<"add", ADDi>, ISA_MIPS1_NOT_32R6_64R6; defm : OneOrTwoOperandMacroImmediateAlias<"addu", ADDiu>, ISA_MIPS1; defm : OneOrTwoOperandMacroImmediateAlias<"and", ANDi>, ISA_MIPS1, GPR_32; defm : OneOrTwoOperandMacroImmediateAlias<"or", ORi>, ISA_MIPS1, GPR_32; defm : OneOrTwoOperandMacroImmediateAlias<"xor", XORi>, ISA_MIPS1, GPR_32; defm : OneOrTwoOperandMacroImmediateAlias<"slt", SLTi>, ISA_MIPS1, GPR_32; defm : OneOrTwoOperandMacroImmediateAlias<"sltu", SLTiu>, ISA_MIPS1, GPR_32; def : MipsInstAlias<"mfgc0 $rt, $rd", (MFGC0 GPR32Opnd:$rt, COP0Opnd:$rd, 0), 0>, ISA_MIPS32R5, ASE_VIRT; def : MipsInstAlias<"mtgc0 $rt, $rd", (MTGC0 COP0Opnd:$rd, GPR32Opnd:$rt, 0), 0>, ISA_MIPS32R5, ASE_VIRT; def : MipsInstAlias<"mfhgc0 $rt, $rd", (MFHGC0 GPR32Opnd:$rt, COP0Opnd:$rd, 0), 0>, ISA_MIPS32R5, ASE_VIRT; def : MipsInstAlias<"mthgc0 $rt, $rd", (MTHGC0 COP0Opnd:$rd, GPR32Opnd:$rt, 0), 0>, ISA_MIPS32R5, ASE_VIRT; def : MipsInstAlias<"mfc0 $rt, $rd", (MFC0 GPR32Opnd:$rt, COP0Opnd:$rd, 0), 0>, ISA_MIPS1; def : MipsInstAlias<"mtc0 $rt, $rd", (MTC0 COP0Opnd:$rd, GPR32Opnd:$rt, 0), 0>, ISA_MIPS1; def : MipsInstAlias<"mfc2 $rt, $rd", (MFC2 GPR32Opnd:$rt, COP2Opnd:$rd, 0), 0>, ISA_MIPS1; def : MipsInstAlias<"mtc2 $rt, $rd", (MTC2 COP2Opnd:$rd, GPR32Opnd:$rt, 0), 0>, ISA_MIPS1; def : MipsInstAlias<"b $offset", (BEQ ZERO, ZERO, brtarget:$offset), 0>, ISA_MIPS1; def : MipsInstAlias<"bnez $rs,$offset", (BNE GPR32Opnd:$rs, ZERO, brtarget:$offset), 0>, ISA_MIPS1; def : MipsInstAlias<"bnezl $rs,$offset", (BNEL GPR32Opnd:$rs, ZERO, brtarget:$offset), 0>, ISA_MIPS2; def : MipsInstAlias<"beqz $rs,$offset", (BEQ GPR32Opnd:$rs, ZERO, brtarget:$offset), 0>, ISA_MIPS1; def : MipsInstAlias<"beqzl $rs,$offset", (BEQL GPR32Opnd:$rs, ZERO, brtarget:$offset), 0>, ISA_MIPS2; def : MipsInstAlias<"syscall", (SYSCALL 0), 1>, ISA_MIPS1; def : MipsInstAlias<"break", (BREAK 0, 0), 1>, ISA_MIPS1; def : MipsInstAlias<"break $imm", (BREAK uimm10:$imm, 0), 1>, ISA_MIPS1; def : MipsInstAlias<"ei", (EI ZERO), 1>, ISA_MIPS32R2; def : MipsInstAlias<"di", (DI ZERO), 1>, ISA_MIPS32R2; def : MipsInstAlias<"teq $rs, $rt", (TEQ GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MIPS2; def : MipsInstAlias<"tge $rs, $rt", (TGE GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MIPS2; def : MipsInstAlias<"tgeu $rs, $rt", (TGEU GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MIPS2; def : MipsInstAlias<"tlt $rs, $rt", (TLT GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MIPS2; def : MipsInstAlias<"tltu $rs, $rt", (TLTU GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MIPS2; def : MipsInstAlias<"tne $rs, $rt", (TNE GPR32Opnd:$rs, GPR32Opnd:$rt, 0), 1>, ISA_MIPS2; def : MipsInstAlias<"rdhwr $rt, $rs", (RDHWR GPR32Opnd:$rt, HWRegsOpnd:$rs, 0), 1>, ISA_MIPS1; } def : MipsInstAlias<"sub, $rd, $rs, $imm", (ADDi GPR32Opnd:$rd, GPR32Opnd:$rs, InvertedImOperand:$imm), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"sub $rs, $imm", (ADDi GPR32Opnd:$rs, GPR32Opnd:$rs, InvertedImOperand:$imm), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"subu, $rd, $rs, $imm", (ADDiu GPR32Opnd:$rd, GPR32Opnd:$rs, InvertedImOperand:$imm), 0>; def : MipsInstAlias<"subu $rs, $imm", (ADDiu GPR32Opnd:$rs, GPR32Opnd:$rs, InvertedImOperand:$imm), 0>; let AdditionalPredicates = [NotInMicroMips] in { def : MipsInstAlias<"sll $rd, $rt, $rs", (SLLV GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; def : MipsInstAlias<"sra $rd, $rt, $rs", (SRAV GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; def : MipsInstAlias<"srl $rd, $rt, $rs", (SRLV GPR32Opnd:$rd, GPR32Opnd:$rt, GPR32Opnd:$rs), 0>; def : MipsInstAlias<"sll $rd, $rt", (SLLV GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rt), 0>; def : MipsInstAlias<"sra $rd, $rt", (SRAV GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rt), 0>; def : MipsInstAlias<"srl $rd, $rt", (SRLV GPR32Opnd:$rd, GPR32Opnd:$rd, GPR32Opnd:$rt), 0>; def : MipsInstAlias<"seh $rd", (SEH GPR32Opnd:$rd, GPR32Opnd:$rd), 0>, ISA_MIPS32R2; def : MipsInstAlias<"seb $rd", (SEB GPR32Opnd:$rd, GPR32Opnd:$rd), 0>, ISA_MIPS32R2; } def : MipsInstAlias<"sdbbp", (SDBBP 0)>, ISA_MIPS32_NOT_32R6_64R6; let AdditionalPredicates = [NotInMicroMips] in def : MipsInstAlias<"sync", (SYNC 0), 1>, ISA_MIPS2; def : MipsInstAlias<"mulo $rs, $rt", (MULOMacro GPR32Opnd:$rs, GPR32Opnd:$rs, GPR32Opnd:$rt), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"mulou $rs, $rt", (MULOUMacro GPR32Opnd:$rs, GPR32Opnd:$rs, GPR32Opnd:$rt), 0>, ISA_MIPS1_NOT_32R6_64R6; let AdditionalPredicates = [NotInMicroMips] in def : MipsInstAlias<"hypcall", (HYPCALL 0), 1>, ISA_MIPS32R5, ASE_VIRT; //===----------------------------------------------------------------------===// // Assembler Pseudo Instructions //===----------------------------------------------------------------------===// // We use uimm32_coerced to accept a 33 bit signed number that is rendered into // a 32 bit number. class LoadImmediate32 : MipsAsmPseudoInst<(outs RO:$rt), (ins Od:$imm32), !strconcat(instr_asm, "\t$rt, $imm32")> ; def LoadImm32 : LoadImmediate32<"li", uimm32_coerced, GPR32Opnd>; class LoadAddressFromReg32 : MipsAsmPseudoInst<(outs RO:$rt), (ins MemOpnd:$addr), !strconcat(instr_asm, "\t$rt, $addr")> ; def LoadAddrReg32 : LoadAddressFromReg32<"la", mem, GPR32Opnd>; class LoadAddressFromImm32 : MipsAsmPseudoInst<(outs RO:$rt), (ins Od:$imm32), !strconcat(instr_asm, "\t$rt, $imm32")> ; def LoadAddrImm32 : LoadAddressFromImm32<"la", i32imm, GPR32Opnd>; def JalTwoReg : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs), "jal\t$rd, $rs"> ; def JalOneReg : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs), "jal\t$rs"> ; class NORIMM_DESC_BASE : MipsAsmPseudoInst<(outs RO:$rs), (ins RO:$rt, Imm:$imm), "nor\t$rs, $rt, $imm">; def NORImm : NORIMM_DESC_BASE, GPR_32; def : MipsInstAlias<"nor\t$rs, $imm", (NORImm GPR32Opnd:$rs, GPR32Opnd:$rs, simm32_relaxed:$imm)>, GPR_32; let hasDelaySlot = 1, isCTI = 1 in { def BneImm : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins imm64:$imm64, brtarget:$offset), "bne\t$rt, $imm64, $offset">; def BeqImm : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins imm64:$imm64, brtarget:$offset), "beq\t$rt, $imm64, $offset">; class CondBranchPseudo : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, GPR32Opnd:$rt, brtarget:$offset), !strconcat(instr_asm, "\t$rs, $rt, $offset")>; } def BLT : CondBranchPseudo<"blt">; def BLE : CondBranchPseudo<"ble">; def BGE : CondBranchPseudo<"bge">; def BGT : CondBranchPseudo<"bgt">; def BLTU : CondBranchPseudo<"bltu">; def BLEU : CondBranchPseudo<"bleu">; def BGEU : CondBranchPseudo<"bgeu">; def BGTU : CondBranchPseudo<"bgtu">; def BLTL : CondBranchPseudo<"bltl">, ISA_MIPS2_NOT_32R6_64R6; def BLEL : CondBranchPseudo<"blel">, ISA_MIPS2_NOT_32R6_64R6; def BGEL : CondBranchPseudo<"bgel">, ISA_MIPS2_NOT_32R6_64R6; def BGTL : CondBranchPseudo<"bgtl">, ISA_MIPS2_NOT_32R6_64R6; def BLTUL: CondBranchPseudo<"bltul">, ISA_MIPS2_NOT_32R6_64R6; def BLEUL: CondBranchPseudo<"bleul">, ISA_MIPS2_NOT_32R6_64R6; def BGEUL: CondBranchPseudo<"bgeul">, ISA_MIPS2_NOT_32R6_64R6; def BGTUL: CondBranchPseudo<"bgtul">, ISA_MIPS2_NOT_32R6_64R6; let isCTI = 1 in class CondBranchImmPseudo : MipsAsmPseudoInst<(outs), (ins GPR32Opnd:$rs, imm64:$imm, brtarget:$offset), !strconcat(instr_asm, "\t$rs, $imm, $offset")>; def BEQLImmMacro : CondBranchImmPseudo<"beql">, ISA_MIPS2_NOT_32R6_64R6; def BNELImmMacro : CondBranchImmPseudo<"bnel">, ISA_MIPS2_NOT_32R6_64R6; def BLTImmMacro : CondBranchImmPseudo<"blt">; def BLEImmMacro : CondBranchImmPseudo<"ble">; def BGEImmMacro : CondBranchImmPseudo<"bge">; def BGTImmMacro : CondBranchImmPseudo<"bgt">; def BLTUImmMacro : CondBranchImmPseudo<"bltu">; def BLEUImmMacro : CondBranchImmPseudo<"bleu">; def BGEUImmMacro : CondBranchImmPseudo<"bgeu">; def BGTUImmMacro : CondBranchImmPseudo<"bgtu">; def BLTLImmMacro : CondBranchImmPseudo<"bltl">, ISA_MIPS2_NOT_32R6_64R6; def BLELImmMacro : CondBranchImmPseudo<"blel">, ISA_MIPS2_NOT_32R6_64R6; def BGELImmMacro : CondBranchImmPseudo<"bgel">, ISA_MIPS2_NOT_32R6_64R6; def BGTLImmMacro : CondBranchImmPseudo<"bgtl">, ISA_MIPS2_NOT_32R6_64R6; def BLTULImmMacro : CondBranchImmPseudo<"bltul">, ISA_MIPS2_NOT_32R6_64R6; def BLEULImmMacro : CondBranchImmPseudo<"bleul">, ISA_MIPS2_NOT_32R6_64R6; def BGEULImmMacro : CondBranchImmPseudo<"bgeul">, ISA_MIPS2_NOT_32R6_64R6; def BGTULImmMacro : CondBranchImmPseudo<"bgtul">, ISA_MIPS2_NOT_32R6_64R6; // FIXME: Predicates are removed because instructions are matched regardless of // predicates, because PredicateControl was not in the hierarchy. This was // done to emit more precise error message from expansion function. // Once the tablegen-erated errors are made better, this needs to be fixed and // predicates needs to be restored. def SDivMacro : MipsAsmPseudoInst<(outs GPR32NonZeroOpnd:$rd), (ins GPR32Opnd:$rs, GPR32Opnd:$rt), "div\t$rd, $rs, $rt">, ISA_MIPS1_NOT_32R6_64R6; def SDivIMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, simm32:$imm), "div\t$rd, $rs, $imm">, ISA_MIPS1_NOT_32R6_64R6; def UDivMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, GPR32Opnd:$rt), "divu\t$rd, $rs, $rt">, ISA_MIPS1_NOT_32R6_64R6; def UDivIMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rd), (ins GPR32Opnd:$rs, simm32:$imm), "divu\t$rd, $rs, $imm">, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"div $rs, $rt", (SDIV GPR32ZeroOpnd:$rs, GPR32Opnd:$rt), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"div $rs, $rt", (SDivMacro GPR32NonZeroOpnd:$rs, GPR32NonZeroOpnd:$rs, GPR32Opnd:$rt), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"div $rd, $imm", (SDivIMacro GPR32Opnd:$rd, GPR32Opnd:$rd, simm32:$imm), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"divu $rt, $rs", (UDIV GPR32ZeroOpnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"divu $rt, $rs", (UDivMacro GPR32NonZeroOpnd:$rt, GPR32NonZeroOpnd:$rt, GPR32Opnd:$rs), 0>, ISA_MIPS1_NOT_32R6_64R6; def : MipsInstAlias<"divu $rd, $imm", (UDivIMacro GPR32Opnd:$rd, GPR32Opnd:$rd, simm32:$imm), 0>, ISA_MIPS1_NOT_32R6_64R6; def Ulh : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem:$addr), "ulh\t$rt, $addr">; //, ISA_MIPS1_NOT_32R6_64R6; def Ulhu : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem:$addr), "ulhu\t$rt, $addr">; //, ISA_MIPS1_NOT_32R6_64R6; def Ulw : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem:$addr), "ulw\t$rt, $addr">; //, ISA_MIPS1_NOT_32R6_64R6; def Ush : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem:$addr), "ush\t$rt, $addr">; //, ISA_MIPS1_NOT_32R6_64R6; def Usw : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem:$addr), "usw\t$rt, $addr">; //, ISA_MIPS1_NOT_32R6_64R6; def LDMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem_simm16:$addr), "ld $rt, $addr">, ISA_MIPS1_NOT_MIPS3; def SDMacro : MipsAsmPseudoInst<(outs GPR32Opnd:$rt), (ins mem_simm16:$addr), "sd $rt, $addr">, ISA_MIPS1_NOT_MIPS3; //===----------------------------------------------------------------------===// // Arbitrary patterns that map to one or more instructions //===----------------------------------------------------------------------===// // Load/store pattern templates. class LoadRegImmPat : MipsPat<(ValTy (Node addrRegImm:$a)), (LoadInst addrRegImm:$a)>; class StoreRegImmPat : MipsPat<(store ValTy:$v, addrRegImm:$a), (StoreInst ValTy:$v, addrRegImm:$a)>; // Materialize constants. multiclass MaterializeImms { // Constant synthesis previously relied on the ordering of the patterns below. // By making the predicates they use non-overlapping, the patterns were // reordered so that the effect of the newly introduced predicates can be // observed. // Arbitrary immediates def : MipsPat<(VT LUiORiPred:$imm), (ORiOp (LUiOp (HI16 imm:$imm)), (LO16 imm:$imm))>; // Bits 32-16 set, sign/zero extended. def : MipsPat<(VT LUiPred:$imm), (LUiOp (HI16 imm:$imm))>; // Small immediates def : MipsPat<(VT ORiPred:$imm), (ORiOp ZEROReg, imm:$imm)>; def : MipsPat<(VT immSExt16:$imm), (ADDiuOp ZEROReg, imm:$imm)>; } let AdditionalPredicates = [NotInMicroMips] in defm : MaterializeImms; // Carry MipsPatterns let AdditionalPredicates = [NotInMicroMips] in { def : MipsPat<(subc GPR32:$lhs, GPR32:$rhs), (SUBu GPR32:$lhs, GPR32:$rhs)>; } def : MipsPat<(addc GPR32:$lhs, GPR32:$rhs), (ADDu GPR32:$lhs, GPR32:$rhs)>, ASE_NOT_DSP; def : MipsPat<(addc GPR32:$src, immSExt16:$imm), (ADDiu GPR32:$src, imm:$imm)>, ASE_NOT_DSP; // Support multiplication for pre-Mips32 targets that don't have // the MUL instruction. def : MipsPat<(mul GPR32:$lhs, GPR32:$rhs), (PseudoMFLO (PseudoMULT GPR32:$lhs, GPR32:$rhs))>, ISA_MIPS1_NOT_32R6_64R6; // SYNC def : MipsPat<(MipsSync (i32 immz)), (SYNC 0)>, ISA_MIPS2; // Call def : MipsPat<(MipsJmpLink (i32 texternalsym:$dst)), (JAL texternalsym:$dst)>; //def : MipsPat<(MipsJmpLink GPR32:$dst), // (JALR GPR32:$dst)>; // Tail call let AdditionalPredicates = [NotInMicroMips] in { def : MipsPat<(MipsTailCall (iPTR tglobaladdr:$dst)), (TAILCALL tglobaladdr:$dst)>; def : MipsPat<(MipsTailCall (iPTR texternalsym:$dst)), (TAILCALL texternalsym:$dst)>; } // hi/lo relocs multiclass MipsHiLoRelocs { def : MipsPat<(MipsHi tglobaladdr:$in), (Lui tglobaladdr:$in)>; def : MipsPat<(MipsHi tblockaddress:$in), (Lui tblockaddress:$in)>; def : MipsPat<(MipsHi tjumptable:$in), (Lui tjumptable:$in)>; def : MipsPat<(MipsHi tconstpool:$in), (Lui tconstpool:$in)>; def : MipsPat<(MipsHi tglobaltlsaddr:$in), (Lui tglobaltlsaddr:$in)>; def : MipsPat<(MipsHi texternalsym:$in), (Lui texternalsym:$in)>; def : MipsPat<(MipsLo tglobaladdr:$in), (Addiu ZeroReg, tglobaladdr:$in)>; def : MipsPat<(MipsLo tblockaddress:$in), (Addiu ZeroReg, tblockaddress:$in)>; def : MipsPat<(MipsLo tjumptable:$in), (Addiu ZeroReg, tjumptable:$in)>; def : MipsPat<(MipsLo tconstpool:$in), (Addiu ZeroReg, tconstpool:$in)>; def : MipsPat<(MipsLo tglobaltlsaddr:$in), (Addiu ZeroReg, tglobaltlsaddr:$in)>; def : MipsPat<(MipsLo texternalsym:$in), (Addiu ZeroReg, texternalsym:$in)>; def : MipsPat<(add GPROpnd:$hi, (MipsLo tglobaladdr:$lo)), (Addiu GPROpnd:$hi, tglobaladdr:$lo)>; def : MipsPat<(add GPROpnd:$hi, (MipsLo tblockaddress:$lo)), (Addiu GPROpnd:$hi, tblockaddress:$lo)>; def : MipsPat<(add GPROpnd:$hi, (MipsLo tjumptable:$lo)), (Addiu GPROpnd:$hi, tjumptable:$lo)>; def : MipsPat<(add GPROpnd:$hi, (MipsLo tconstpool:$lo)), (Addiu GPROpnd:$hi, tconstpool:$lo)>; def : MipsPat<(add GPROpnd:$hi, (MipsLo tglobaltlsaddr:$lo)), (Addiu GPROpnd:$hi, tglobaltlsaddr:$lo)>; } let AdditionalPredicates = [NotInMicroMips] in { defm : MipsHiLoRelocs, ISA_MIPS1; def : MipsPat<(MipsGotHi tglobaladdr:$in), (LUi tglobaladdr:$in)>, ISA_MIPS1; def : MipsPat<(MipsGotHi texternalsym:$in), (LUi texternalsym:$in)>, ISA_MIPS1; // gp_rel relocs def : MipsPat<(add GPR32:$gp, (MipsGPRel tglobaladdr:$in)), (ADDiu GPR32:$gp, tglobaladdr:$in)>, ISA_MIPS1, ABI_NOT_N64; def : MipsPat<(add GPR32:$gp, (MipsGPRel tconstpool:$in)), (ADDiu GPR32:$gp, tconstpool:$in)>, ISA_MIPS1, ABI_NOT_N64; // wrapper_pic class WrapperPat: MipsPat<(MipsWrapper RC:$gp, node:$in), (ADDiuOp RC:$gp, node:$in)>; def : WrapperPat, ISA_MIPS1; def : WrapperPat, ISA_MIPS1; def : WrapperPat, ISA_MIPS1; def : WrapperPat, ISA_MIPS1; def : WrapperPat, ISA_MIPS1; def : WrapperPat, ISA_MIPS1; // Mips does not have "not", so we expand our way def : MipsPat<(not GPR32:$in), (NOR GPR32Opnd:$in, ZERO)>, ISA_MIPS1; } // extended loads let AdditionalPredicates = [NotInMicroMips] in { def : MipsPat<(i32 (extloadi1 addr:$src)), (LBu addr:$src)>; def : MipsPat<(i32 (extloadi8 addr:$src)), (LBu addr:$src)>; def : MipsPat<(i32 (extloadi16 addr:$src)), (LHu addr:$src)>; } // peepholes def : MipsPat<(store (i32 0), addr:$dst), (SW ZERO, addr:$dst)>; // brcond patterns multiclass BrcondPats { def : MipsPat<(brcond (i32 (setne RC:$lhs, 0)), bb:$dst), (BNEOp RC:$lhs, ZEROReg, bb:$dst)>; def : MipsPat<(brcond (i32 (seteq RC:$lhs, 0)), bb:$dst), (BEQOp RC:$lhs, ZEROReg, bb:$dst)>; def : MipsPat<(brcond (i32 (setge RC:$lhs, RC:$rhs)), bb:$dst), (BEQOp1 (SLTOp RC:$lhs, RC:$rhs), ZERO, bb:$dst)>; def : MipsPat<(brcond (i32 (setuge RC:$lhs, RC:$rhs)), bb:$dst), (BEQOp1 (SLTuOp RC:$lhs, RC:$rhs), ZERO, bb:$dst)>; def : MipsPat<(brcond (i32 (setge RC:$lhs, immSExt16:$rhs)), bb:$dst), (BEQOp1 (SLTiOp RC:$lhs, immSExt16:$rhs), ZERO, bb:$dst)>; def : MipsPat<(brcond (i32 (setuge RC:$lhs, immSExt16:$rhs)), bb:$dst), (BEQOp1 (SLTiuOp RC:$lhs, immSExt16:$rhs), ZERO, bb:$dst)>; def : MipsPat<(brcond (i32 (setgt RC:$lhs, immSExt16Plus1:$rhs)), bb:$dst), (BEQOp1 (SLTiOp RC:$lhs, (Plus1 imm:$rhs)), ZERO, bb:$dst)>; def : MipsPat<(brcond (i32 (setugt RC:$lhs, immSExt16Plus1:$rhs)), bb:$dst), (BEQOp1 (SLTiuOp RC:$lhs, (Plus1 imm:$rhs)), ZERO, bb:$dst)>; def : MipsPat<(brcond (i32 (setle RC:$lhs, RC:$rhs)), bb:$dst), (BEQOp1 (SLTOp RC:$rhs, RC:$lhs), ZERO, bb:$dst)>; def : MipsPat<(brcond (i32 (setule RC:$lhs, RC:$rhs)), bb:$dst), (BEQOp1 (SLTuOp RC:$rhs, RC:$lhs), ZERO, bb:$dst)>; def : MipsPat<(brcond RC:$cond, bb:$dst), (BNEOp RC:$cond, ZEROReg, bb:$dst)>; } let AdditionalPredicates = [NotInMicroMips] in { defm : BrcondPats; } def : MipsPat<(brcond (i32 (setlt i32:$lhs, 1)), bb:$dst), (BLEZ i32:$lhs, bb:$dst)>; def : MipsPat<(brcond (i32 (setgt i32:$lhs, -1)), bb:$dst), (BGEZ i32:$lhs, bb:$dst)>; // setcc patterns multiclass SeteqPats { def : MipsPat<(seteq RC:$lhs, 0), (SLTiuOp RC:$lhs, 1)>; def : MipsPat<(setne RC:$lhs, 0), (SLTuOp ZEROReg, RC:$lhs)>; def : MipsPat<(seteq RC:$lhs, RC:$rhs), (SLTiuOp (XOROp RC:$lhs, RC:$rhs), 1)>; def : MipsPat<(setne RC:$lhs, RC:$rhs), (SLTuOp ZEROReg, (XOROp RC:$lhs, RC:$rhs))>; } multiclass SetlePats { def : MipsPat<(setle RC:$lhs, RC:$rhs), (XORiOp (SLTOp RC:$rhs, RC:$lhs), 1)>; def : MipsPat<(setule RC:$lhs, RC:$rhs), (XORiOp (SLTuOp RC:$rhs, RC:$lhs), 1)>; } multiclass SetgtPats { def : MipsPat<(setgt RC:$lhs, RC:$rhs), (SLTOp RC:$rhs, RC:$lhs)>; def : MipsPat<(setugt RC:$lhs, RC:$rhs), (SLTuOp RC:$rhs, RC:$lhs)>; } multiclass SetgePats { def : MipsPat<(setge RC:$lhs, RC:$rhs), (XORiOp (SLTOp RC:$lhs, RC:$rhs), 1)>; def : MipsPat<(setuge RC:$lhs, RC:$rhs), (XORiOp (SLTuOp RC:$lhs, RC:$rhs), 1)>; } multiclass SetgeImmPats { def : MipsPat<(setge RC:$lhs, immSExt16:$rhs), (XORiOp (SLTiOp RC:$lhs, immSExt16:$rhs), 1)>; def : MipsPat<(setuge RC:$lhs, immSExt16:$rhs), (XORiOp (SLTiuOp RC:$lhs, immSExt16:$rhs), 1)>; } let AdditionalPredicates = [NotInMicroMips] in { defm : SeteqPats; defm : SetlePats; defm : SetgtPats; defm : SetgePats; defm : SetgeImmPats; // bswap pattern def : MipsPat<(bswap GPR32:$rt), (ROTR (WSBH GPR32:$rt), 16)>, ISA_MIPS32R2; } // Load halfword/word patterns. let AddedComplexity = 40 in { let AdditionalPredicates = [NotInMicroMips] in { def : LoadRegImmPat, ISA_MIPS1; def : LoadRegImmPat, ISA_MIPS1; def : LoadRegImmPat, ISA_MIPS1; def : LoadRegImmPat, ISA_MIPS1; def : LoadRegImmPat, ISA_MIPS1; } } // Atomic load patterns. def : MipsPat<(atomic_load_8 addr:$a), (LB addr:$a)>; let AdditionalPredicates = [NotInMicroMips] in { def : MipsPat<(atomic_load_16 addr:$a), (LH addr:$a)>; } def : MipsPat<(atomic_load_32 addr:$a), (LW addr:$a)>; // Atomic store patterns. def : MipsPat<(atomic_store_8 addr:$a, GPR32:$v), (SB GPR32:$v, addr:$a)>; def : MipsPat<(atomic_store_16 addr:$a, GPR32:$v), (SH GPR32:$v, addr:$a)>; def : MipsPat<(atomic_store_32 addr:$a, GPR32:$v), (SW GPR32:$v, addr:$a)>; //===----------------------------------------------------------------------===// // Floating Point Support //===----------------------------------------------------------------------===// include "MipsInstrFPU.td" include "Mips64InstrInfo.td" include "MipsCondMov.td" include "Mips32r6InstrInfo.td" include "Mips64r6InstrInfo.td" // // Mips16 include "Mips16InstrFormats.td" include "Mips16InstrInfo.td" // DSP include "MipsDSPInstrFormats.td" include "MipsDSPInstrInfo.td" // MSA include "MipsMSAInstrFormats.td" include "MipsMSAInstrInfo.td" // EVA include "MipsEVAInstrFormats.td" include "MipsEVAInstrInfo.td" // MT include "MipsMTInstrFormats.td" include "MipsMTInstrInfo.td" // Micromips include "MicroMipsInstrFormats.td" include "MicroMipsInstrInfo.td" include "MicroMipsInstrFPU.td" // Micromips r6 include "MicroMips32r6InstrFormats.td" include "MicroMips32r6InstrInfo.td" // Micromips DSP include "MicroMipsDSPInstrFormats.td" include "MicroMipsDSPInstrInfo.td" Index: llvm/trunk/lib/Target/Mips/MipsISelLowering.cpp =================================================================== --- llvm/trunk/lib/Target/Mips/MipsISelLowering.cpp (revision 336327) +++ llvm/trunk/lib/Target/Mips/MipsISelLowering.cpp (revision 336328) @@ -1,4406 +1,4353 @@ //===- MipsISelLowering.cpp - Mips DAG Lowering Implementation ------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This file defines the interfaces that Mips uses to lower LLVM code into a // selection DAG. // //===----------------------------------------------------------------------===// #include "MipsISelLowering.h" #include "InstPrinter/MipsInstPrinter.h" #include "MCTargetDesc/MipsBaseInfo.h" #include "MCTargetDesc/MipsMCTargetDesc.h" #include "MipsCCState.h" #include "MipsInstrInfo.h" #include "MipsMachineFunction.h" #include "MipsRegisterInfo.h" #include "MipsSubtarget.h" #include "MipsTargetMachine.h" #include "MipsTargetObjectFile.h" #include "llvm/ADT/APFloat.h" #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/Statistic.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/CodeGen/CallingConvLower.h" #include "llvm/CodeGen/FunctionLoweringInfo.h" #include "llvm/CodeGen/ISDOpcodes.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineInstrBuilder.h" #include "llvm/CodeGen/MachineJumpTableInfo.h" #include "llvm/CodeGen/MachineMemOperand.h" #include "llvm/CodeGen/MachineOperand.h" #include "llvm/CodeGen/MachineRegisterInfo.h" #include "llvm/CodeGen/RuntimeLibcalls.h" #include "llvm/CodeGen/SelectionDAG.h" #include "llvm/CodeGen/SelectionDAGNodes.h" #include "llvm/CodeGen/TargetFrameLowering.h" #include "llvm/CodeGen/TargetInstrInfo.h" #include "llvm/CodeGen/TargetRegisterInfo.h" #include "llvm/CodeGen/ValueTypes.h" #include "llvm/IR/CallingConv.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DataLayout.h" #include "llvm/IR/DebugLoc.h" #include "llvm/IR/DerivedTypes.h" #include "llvm/IR/Function.h" #include "llvm/IR/GlobalValue.h" #include "llvm/IR/Type.h" #include "llvm/IR/Value.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/Support/Casting.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/MachineValueType.h" #include "llvm/Support/MathExtras.h" #include "llvm/Target/TargetMachine.h" #include "llvm/Target/TargetOptions.h" #include #include #include #include #include #include #include #include using namespace llvm; #define DEBUG_TYPE "mips-lower" STATISTIC(NumTailCalls, "Number of tail calls"); static cl::opt LargeGOT("mxgot", cl::Hidden, cl::desc("MIPS: Enable GOT larger than 64k."), cl::init(false)); static cl::opt NoZeroDivCheck("mno-check-zero-division", cl::Hidden, cl::desc("MIPS: Don't trap on integer division by zero."), cl::init(false)); static const MCPhysReg Mips64DPRegs[8] = { Mips::D12_64, Mips::D13_64, Mips::D14_64, Mips::D15_64, Mips::D16_64, Mips::D17_64, Mips::D18_64, Mips::D19_64 }; // If I is a shifted mask, set the size (Size) and the first bit of the // mask (Pos), and return true. // For example, if I is 0x003ff800, (Pos, Size) = (11, 11). static bool isShiftedMask(uint64_t I, uint64_t &Pos, uint64_t &Size) { if (!isShiftedMask_64(I)) return false; Size = countPopulation(I); Pos = countTrailingZeros(I); return true; } // The MIPS MSA ABI passes vector arguments in the integer register set. // The number of integer registers used is dependant on the ABI used. MVT MipsTargetLowering::getRegisterTypeForCallingConv(LLVMContext &Context, EVT VT) const { if (VT.isVector()) { if (Subtarget.isABI_O32()) { return MVT::i32; } else { return (VT.getSizeInBits() == 32) ? MVT::i32 : MVT::i64; } } return MipsTargetLowering::getRegisterType(Context, VT); } unsigned MipsTargetLowering::getNumRegistersForCallingConv(LLVMContext &Context, EVT VT) const { if (VT.isVector()) return std::max((VT.getSizeInBits() / (Subtarget.isABI_O32() ? 32 : 64)), 1U); return MipsTargetLowering::getNumRegisters(Context, VT); } unsigned MipsTargetLowering::getVectorTypeBreakdownForCallingConv( LLVMContext &Context, EVT VT, EVT &IntermediateVT, unsigned &NumIntermediates, MVT &RegisterVT) const { // Break down vector types to either 2 i64s or 4 i32s. RegisterVT = getRegisterTypeForCallingConv(Context, VT) ; IntermediateVT = RegisterVT; NumIntermediates = VT.getSizeInBits() < RegisterVT.getSizeInBits() ? VT.getVectorNumElements() : VT.getSizeInBits() / RegisterVT.getSizeInBits(); return NumIntermediates; } SDValue MipsTargetLowering::getGlobalReg(SelectionDAG &DAG, EVT Ty) const { MipsFunctionInfo *FI = DAG.getMachineFunction().getInfo(); return DAG.getRegister(FI->getGlobalBaseReg(), Ty); } SDValue MipsTargetLowering::getTargetNode(GlobalAddressSDNode *N, EVT Ty, SelectionDAG &DAG, unsigned Flag) const { return DAG.getTargetGlobalAddress(N->getGlobal(), SDLoc(N), Ty, 0, Flag); } SDValue MipsTargetLowering::getTargetNode(ExternalSymbolSDNode *N, EVT Ty, SelectionDAG &DAG, unsigned Flag) const { return DAG.getTargetExternalSymbol(N->getSymbol(), Ty, Flag); } SDValue MipsTargetLowering::getTargetNode(BlockAddressSDNode *N, EVT Ty, SelectionDAG &DAG, unsigned Flag) const { return DAG.getTargetBlockAddress(N->getBlockAddress(), Ty, 0, Flag); } SDValue MipsTargetLowering::getTargetNode(JumpTableSDNode *N, EVT Ty, SelectionDAG &DAG, unsigned Flag) const { return DAG.getTargetJumpTable(N->getIndex(), Ty, Flag); } SDValue MipsTargetLowering::getTargetNode(ConstantPoolSDNode *N, EVT Ty, SelectionDAG &DAG, unsigned Flag) const { return DAG.getTargetConstantPool(N->getConstVal(), Ty, N->getAlignment(), N->getOffset(), Flag); } const char *MipsTargetLowering::getTargetNodeName(unsigned Opcode) const { switch ((MipsISD::NodeType)Opcode) { case MipsISD::FIRST_NUMBER: break; case MipsISD::JmpLink: return "MipsISD::JmpLink"; case MipsISD::TailCall: return "MipsISD::TailCall"; case MipsISD::Highest: return "MipsISD::Highest"; case MipsISD::Higher: return "MipsISD::Higher"; case MipsISD::Hi: return "MipsISD::Hi"; case MipsISD::Lo: return "MipsISD::Lo"; case MipsISD::GotHi: return "MipsISD::GotHi"; case MipsISD::GPRel: return "MipsISD::GPRel"; case MipsISD::ThreadPointer: return "MipsISD::ThreadPointer"; case MipsISD::Ret: return "MipsISD::Ret"; case MipsISD::ERet: return "MipsISD::ERet"; case MipsISD::EH_RETURN: return "MipsISD::EH_RETURN"; case MipsISD::FMS: return "MipsISD::FMS"; case MipsISD::FPBrcond: return "MipsISD::FPBrcond"; case MipsISD::FPCmp: return "MipsISD::FPCmp"; case MipsISD::FSELECT: return "MipsISD::FSELECT"; case MipsISD::MTC1_D64: return "MipsISD::MTC1_D64"; case MipsISD::CMovFP_T: return "MipsISD::CMovFP_T"; case MipsISD::CMovFP_F: return "MipsISD::CMovFP_F"; case MipsISD::TruncIntFP: return "MipsISD::TruncIntFP"; case MipsISD::MFHI: return "MipsISD::MFHI"; case MipsISD::MFLO: return "MipsISD::MFLO"; case MipsISD::MTLOHI: return "MipsISD::MTLOHI"; case MipsISD::Mult: return "MipsISD::Mult"; case MipsISD::Multu: return "MipsISD::Multu"; case MipsISD::MAdd: return "MipsISD::MAdd"; case MipsISD::MAddu: return "MipsISD::MAddu"; case MipsISD::MSub: return "MipsISD::MSub"; case MipsISD::MSubu: return "MipsISD::MSubu"; case MipsISD::DivRem: return "MipsISD::DivRem"; case MipsISD::DivRemU: return "MipsISD::DivRemU"; case MipsISD::DivRem16: return "MipsISD::DivRem16"; case MipsISD::DivRemU16: return "MipsISD::DivRemU16"; case MipsISD::BuildPairF64: return "MipsISD::BuildPairF64"; case MipsISD::ExtractElementF64: return "MipsISD::ExtractElementF64"; case MipsISD::Wrapper: return "MipsISD::Wrapper"; case MipsISD::DynAlloc: return "MipsISD::DynAlloc"; case MipsISD::Sync: return "MipsISD::Sync"; case MipsISD::Ext: return "MipsISD::Ext"; case MipsISD::Ins: return "MipsISD::Ins"; case MipsISD::CIns: return "MipsISD::CIns"; case MipsISD::LWL: return "MipsISD::LWL"; case MipsISD::LWR: return "MipsISD::LWR"; case MipsISD::SWL: return "MipsISD::SWL"; case MipsISD::SWR: return "MipsISD::SWR"; case MipsISD::LDL: return "MipsISD::LDL"; case MipsISD::LDR: return "MipsISD::LDR"; case MipsISD::SDL: return "MipsISD::SDL"; case MipsISD::SDR: return "MipsISD::SDR"; case MipsISD::EXTP: return "MipsISD::EXTP"; case MipsISD::EXTPDP: return "MipsISD::EXTPDP"; case MipsISD::EXTR_S_H: return "MipsISD::EXTR_S_H"; case MipsISD::EXTR_W: return "MipsISD::EXTR_W"; case MipsISD::EXTR_R_W: return "MipsISD::EXTR_R_W"; case MipsISD::EXTR_RS_W: return "MipsISD::EXTR_RS_W"; case MipsISD::SHILO: return "MipsISD::SHILO"; case MipsISD::MTHLIP: return "MipsISD::MTHLIP"; case MipsISD::MULSAQ_S_W_PH: return "MipsISD::MULSAQ_S_W_PH"; case MipsISD::MAQ_S_W_PHL: return "MipsISD::MAQ_S_W_PHL"; case MipsISD::MAQ_S_W_PHR: return "MipsISD::MAQ_S_W_PHR"; case MipsISD::MAQ_SA_W_PHL: return "MipsISD::MAQ_SA_W_PHL"; case MipsISD::MAQ_SA_W_PHR: return "MipsISD::MAQ_SA_W_PHR"; case MipsISD::DPAU_H_QBL: return "MipsISD::DPAU_H_QBL"; case MipsISD::DPAU_H_QBR: return "MipsISD::DPAU_H_QBR"; case MipsISD::DPSU_H_QBL: return "MipsISD::DPSU_H_QBL"; case MipsISD::DPSU_H_QBR: return "MipsISD::DPSU_H_QBR"; case MipsISD::DPAQ_S_W_PH: return "MipsISD::DPAQ_S_W_PH"; case MipsISD::DPSQ_S_W_PH: return "MipsISD::DPSQ_S_W_PH"; case MipsISD::DPAQ_SA_L_W: return "MipsISD::DPAQ_SA_L_W"; case MipsISD::DPSQ_SA_L_W: return "MipsISD::DPSQ_SA_L_W"; case MipsISD::DPA_W_PH: return "MipsISD::DPA_W_PH"; case MipsISD::DPS_W_PH: return "MipsISD::DPS_W_PH"; case MipsISD::DPAQX_S_W_PH: return "MipsISD::DPAQX_S_W_PH"; case MipsISD::DPAQX_SA_W_PH: return "MipsISD::DPAQX_SA_W_PH"; case MipsISD::DPAX_W_PH: return "MipsISD::DPAX_W_PH"; case MipsISD::DPSX_W_PH: return "MipsISD::DPSX_W_PH"; case MipsISD::DPSQX_S_W_PH: return "MipsISD::DPSQX_S_W_PH"; case MipsISD::DPSQX_SA_W_PH: return "MipsISD::DPSQX_SA_W_PH"; case MipsISD::MULSA_W_PH: return "MipsISD::MULSA_W_PH"; case MipsISD::MULT: return "MipsISD::MULT"; case MipsISD::MULTU: return "MipsISD::MULTU"; case MipsISD::MADD_DSP: return "MipsISD::MADD_DSP"; case MipsISD::MADDU_DSP: return "MipsISD::MADDU_DSP"; case MipsISD::MSUB_DSP: return "MipsISD::MSUB_DSP"; case MipsISD::MSUBU_DSP: return "MipsISD::MSUBU_DSP"; case MipsISD::SHLL_DSP: return "MipsISD::SHLL_DSP"; case MipsISD::SHRA_DSP: return "MipsISD::SHRA_DSP"; case MipsISD::SHRL_DSP: return "MipsISD::SHRL_DSP"; case MipsISD::SETCC_DSP: return "MipsISD::SETCC_DSP"; case MipsISD::SELECT_CC_DSP: return "MipsISD::SELECT_CC_DSP"; case MipsISD::VALL_ZERO: return "MipsISD::VALL_ZERO"; case MipsISD::VANY_ZERO: return "MipsISD::VANY_ZERO"; case MipsISD::VALL_NONZERO: return "MipsISD::VALL_NONZERO"; case MipsISD::VANY_NONZERO: return "MipsISD::VANY_NONZERO"; case MipsISD::VCEQ: return "MipsISD::VCEQ"; case MipsISD::VCLE_S: return "MipsISD::VCLE_S"; case MipsISD::VCLE_U: return "MipsISD::VCLE_U"; case MipsISD::VCLT_S: return "MipsISD::VCLT_S"; case MipsISD::VCLT_U: return "MipsISD::VCLT_U"; case MipsISD::VEXTRACT_SEXT_ELT: return "MipsISD::VEXTRACT_SEXT_ELT"; case MipsISD::VEXTRACT_ZEXT_ELT: return "MipsISD::VEXTRACT_ZEXT_ELT"; case MipsISD::VNOR: return "MipsISD::VNOR"; case MipsISD::VSHF: return "MipsISD::VSHF"; case MipsISD::SHF: return "MipsISD::SHF"; case MipsISD::ILVEV: return "MipsISD::ILVEV"; case MipsISD::ILVOD: return "MipsISD::ILVOD"; case MipsISD::ILVL: return "MipsISD::ILVL"; case MipsISD::ILVR: return "MipsISD::ILVR"; case MipsISD::PCKEV: return "MipsISD::PCKEV"; case MipsISD::PCKOD: return "MipsISD::PCKOD"; case MipsISD::INSVE: return "MipsISD::INSVE"; } return nullptr; } MipsTargetLowering::MipsTargetLowering(const MipsTargetMachine &TM, const MipsSubtarget &STI) : TargetLowering(TM), Subtarget(STI), ABI(TM.getABI()) { // Mips does not have i1 type, so use i32 for // setcc operations results (slt, sgt, ...). setBooleanContents(ZeroOrOneBooleanContent); setBooleanVectorContents(ZeroOrNegativeOneBooleanContent); // The cmp.cond.fmt instruction in MIPS32r6/MIPS64r6 uses 0 and -1 like MSA // does. Integer booleans still use 0 and 1. if (Subtarget.hasMips32r6()) setBooleanContents(ZeroOrOneBooleanContent, ZeroOrNegativeOneBooleanContent); // Load extented operations for i1 types must be promoted for (MVT VT : MVT::integer_valuetypes()) { setLoadExtAction(ISD::EXTLOAD, VT, MVT::i1, Promote); setLoadExtAction(ISD::ZEXTLOAD, VT, MVT::i1, Promote); setLoadExtAction(ISD::SEXTLOAD, VT, MVT::i1, Promote); } // MIPS doesn't have extending float->double load/store. Set LoadExtAction // for f32, f16 for (MVT VT : MVT::fp_valuetypes()) { setLoadExtAction(ISD::EXTLOAD, VT, MVT::f32, Expand); setLoadExtAction(ISD::EXTLOAD, VT, MVT::f16, Expand); } // Set LoadExtAction for f16 vectors to Expand for (MVT VT : MVT::fp_vector_valuetypes()) { MVT F16VT = MVT::getVectorVT(MVT::f16, VT.getVectorNumElements()); if (F16VT.isValid()) setLoadExtAction(ISD::EXTLOAD, VT, F16VT, Expand); } setTruncStoreAction(MVT::f32, MVT::f16, Expand); setTruncStoreAction(MVT::f64, MVT::f16, Expand); setTruncStoreAction(MVT::f64, MVT::f32, Expand); // Used by legalize types to correctly generate the setcc result. // Without this, every float setcc comes with a AND/OR with the result, // we don't want this, since the fpcmp result goes to a flag register, // which is used implicitly by brcond and select operations. AddPromotedToType(ISD::SETCC, MVT::i1, MVT::i32); // Mips Custom Operations setOperationAction(ISD::BR_JT, MVT::Other, Expand); setOperationAction(ISD::GlobalAddress, MVT::i32, Custom); setOperationAction(ISD::BlockAddress, MVT::i32, Custom); setOperationAction(ISD::GlobalTLSAddress, MVT::i32, Custom); setOperationAction(ISD::JumpTable, MVT::i32, Custom); setOperationAction(ISD::ConstantPool, MVT::i32, Custom); setOperationAction(ISD::SELECT, MVT::f32, Custom); setOperationAction(ISD::SELECT, MVT::f64, Custom); setOperationAction(ISD::SELECT, MVT::i32, Custom); setOperationAction(ISD::SETCC, MVT::f32, Custom); setOperationAction(ISD::SETCC, MVT::f64, Custom); setOperationAction(ISD::BRCOND, MVT::Other, Custom); setOperationAction(ISD::FCOPYSIGN, MVT::f32, Custom); setOperationAction(ISD::FCOPYSIGN, MVT::f64, Custom); setOperationAction(ISD::FP_TO_SINT, MVT::i32, Custom); if (Subtarget.isGP64bit()) { setOperationAction(ISD::GlobalAddress, MVT::i64, Custom); setOperationAction(ISD::BlockAddress, MVT::i64, Custom); setOperationAction(ISD::GlobalTLSAddress, MVT::i64, Custom); setOperationAction(ISD::JumpTable, MVT::i64, Custom); setOperationAction(ISD::ConstantPool, MVT::i64, Custom); setOperationAction(ISD::SELECT, MVT::i64, Custom); setOperationAction(ISD::LOAD, MVT::i64, Custom); setOperationAction(ISD::STORE, MVT::i64, Custom); setOperationAction(ISD::FP_TO_SINT, MVT::i64, Custom); setOperationAction(ISD::SHL_PARTS, MVT::i64, Custom); setOperationAction(ISD::SRA_PARTS, MVT::i64, Custom); setOperationAction(ISD::SRL_PARTS, MVT::i64, Custom); } if (!Subtarget.isGP64bit()) { setOperationAction(ISD::SHL_PARTS, MVT::i32, Custom); setOperationAction(ISD::SRA_PARTS, MVT::i32, Custom); setOperationAction(ISD::SRL_PARTS, MVT::i32, Custom); } setOperationAction(ISD::EH_DWARF_CFA, MVT::i32, Custom); if (Subtarget.isGP64bit()) setOperationAction(ISD::EH_DWARF_CFA, MVT::i64, Custom); setOperationAction(ISD::SDIV, MVT::i32, Expand); setOperationAction(ISD::SREM, MVT::i32, Expand); setOperationAction(ISD::UDIV, MVT::i32, Expand); setOperationAction(ISD::UREM, MVT::i32, Expand); setOperationAction(ISD::SDIV, MVT::i64, Expand); setOperationAction(ISD::SREM, MVT::i64, Expand); setOperationAction(ISD::UDIV, MVT::i64, Expand); setOperationAction(ISD::UREM, MVT::i64, Expand); // Operations not directly supported by Mips. setOperationAction(ISD::BR_CC, MVT::f32, Expand); setOperationAction(ISD::BR_CC, MVT::f64, Expand); setOperationAction(ISD::BR_CC, MVT::i32, Expand); setOperationAction(ISD::BR_CC, MVT::i64, Expand); setOperationAction(ISD::SELECT_CC, MVT::i32, Expand); setOperationAction(ISD::SELECT_CC, MVT::i64, Expand); setOperationAction(ISD::SELECT_CC, MVT::f32, Expand); setOperationAction(ISD::SELECT_CC, MVT::f64, Expand); setOperationAction(ISD::UINT_TO_FP, MVT::i32, Expand); setOperationAction(ISD::UINT_TO_FP, MVT::i64, Expand); setOperationAction(ISD::FP_TO_UINT, MVT::i32, Expand); setOperationAction(ISD::FP_TO_UINT, MVT::i64, Expand); setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i1, Expand); if (Subtarget.hasCnMips()) { setOperationAction(ISD::CTPOP, MVT::i32, Legal); setOperationAction(ISD::CTPOP, MVT::i64, Legal); } else { setOperationAction(ISD::CTPOP, MVT::i32, Expand); setOperationAction(ISD::CTPOP, MVT::i64, Expand); } setOperationAction(ISD::CTTZ, MVT::i32, Expand); setOperationAction(ISD::CTTZ, MVT::i64, Expand); setOperationAction(ISD::ROTL, MVT::i32, Expand); setOperationAction(ISD::ROTL, MVT::i64, Expand); setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i32, Expand); setOperationAction(ISD::DYNAMIC_STACKALLOC, MVT::i64, Expand); if (!Subtarget.hasMips32r2()) setOperationAction(ISD::ROTR, MVT::i32, Expand); if (!Subtarget.hasMips64r2()) setOperationAction(ISD::ROTR, MVT::i64, Expand); setOperationAction(ISD::FSIN, MVT::f32, Expand); setOperationAction(ISD::FSIN, MVT::f64, Expand); setOperationAction(ISD::FCOS, MVT::f32, Expand); setOperationAction(ISD::FCOS, MVT::f64, Expand); setOperationAction(ISD::FSINCOS, MVT::f32, Expand); setOperationAction(ISD::FSINCOS, MVT::f64, Expand); setOperationAction(ISD::FPOW, MVT::f32, Expand); setOperationAction(ISD::FPOW, MVT::f64, Expand); setOperationAction(ISD::FLOG, MVT::f32, Expand); setOperationAction(ISD::FLOG2, MVT::f32, Expand); setOperationAction(ISD::FLOG10, MVT::f32, Expand); setOperationAction(ISD::FEXP, MVT::f32, Expand); setOperationAction(ISD::FMA, MVT::f32, Expand); setOperationAction(ISD::FMA, MVT::f64, Expand); setOperationAction(ISD::FREM, MVT::f32, Expand); setOperationAction(ISD::FREM, MVT::f64, Expand); // Lower f16 conversion operations into library calls setOperationAction(ISD::FP16_TO_FP, MVT::f32, Expand); setOperationAction(ISD::FP_TO_FP16, MVT::f32, Expand); setOperationAction(ISD::FP16_TO_FP, MVT::f64, Expand); setOperationAction(ISD::FP_TO_FP16, MVT::f64, Expand); setOperationAction(ISD::EH_RETURN, MVT::Other, Custom); setOperationAction(ISD::VASTART, MVT::Other, Custom); setOperationAction(ISD::VAARG, MVT::Other, Custom); setOperationAction(ISD::VACOPY, MVT::Other, Expand); setOperationAction(ISD::VAEND, MVT::Other, Expand); // Use the default for now setOperationAction(ISD::STACKSAVE, MVT::Other, Expand); setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand); if (!Subtarget.isGP64bit()) { setOperationAction(ISD::ATOMIC_LOAD, MVT::i64, Expand); setOperationAction(ISD::ATOMIC_STORE, MVT::i64, Expand); } if (!Subtarget.hasMips32r2()) { setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i8, Expand); setOperationAction(ISD::SIGN_EXTEND_INREG, MVT::i16, Expand); } // MIPS16 lacks MIPS32's clz and clo instructions. if (!Subtarget.hasMips32() || Subtarget.inMips16Mode()) setOperationAction(ISD::CTLZ, MVT::i32, Expand); if (!Subtarget.hasMips64()) setOperationAction(ISD::CTLZ, MVT::i64, Expand); if (!Subtarget.hasMips32r2()) setOperationAction(ISD::BSWAP, MVT::i32, Expand); if (!Subtarget.hasMips64r2()) setOperationAction(ISD::BSWAP, MVT::i64, Expand); if (Subtarget.isGP64bit()) { setLoadExtAction(ISD::SEXTLOAD, MVT::i64, MVT::i32, Custom); setLoadExtAction(ISD::ZEXTLOAD, MVT::i64, MVT::i32, Custom); setLoadExtAction(ISD::EXTLOAD, MVT::i64, MVT::i32, Custom); setTruncStoreAction(MVT::i64, MVT::i32, Custom); } setOperationAction(ISD::TRAP, MVT::Other, Legal); setTargetDAGCombine(ISD::SDIVREM); setTargetDAGCombine(ISD::UDIVREM); setTargetDAGCombine(ISD::SELECT); setTargetDAGCombine(ISD::AND); setTargetDAGCombine(ISD::OR); setTargetDAGCombine(ISD::ADD); setTargetDAGCombine(ISD::SUB); setTargetDAGCombine(ISD::AssertZext); setTargetDAGCombine(ISD::SHL); if (ABI.IsO32()) { // These libcalls are not available in 32-bit. setLibcallName(RTLIB::SHL_I128, nullptr); setLibcallName(RTLIB::SRL_I128, nullptr); setLibcallName(RTLIB::SRA_I128, nullptr); } setMinFunctionAlignment(Subtarget.isGP64bit() ? 3 : 2); // The arguments on the stack are defined in terms of 4-byte slots on O32 // and 8-byte slots on N32/N64. setMinStackArgumentAlignment((ABI.IsN32() || ABI.IsN64()) ? 8 : 4); setStackPointerRegisterToSaveRestore(ABI.IsN64() ? Mips::SP_64 : Mips::SP); MaxStoresPerMemcpy = 16; isMicroMips = Subtarget.inMicroMipsMode(); } const MipsTargetLowering *MipsTargetLowering::create(const MipsTargetMachine &TM, const MipsSubtarget &STI) { if (STI.inMips16Mode()) return createMips16TargetLowering(TM, STI); return createMipsSETargetLowering(TM, STI); } // Create a fast isel object. FastISel * MipsTargetLowering::createFastISel(FunctionLoweringInfo &funcInfo, const TargetLibraryInfo *libInfo) const { const MipsTargetMachine &TM = static_cast(funcInfo.MF->getTarget()); // We support only the standard encoding [MIPS32,MIPS32R5] ISAs. bool UseFastISel = TM.Options.EnableFastISel && Subtarget.hasMips32() && !Subtarget.hasMips32r6() && !Subtarget.inMips16Mode() && !Subtarget.inMicroMipsMode(); // Disable if either of the following is true: // We do not generate PIC, the ABI is not O32, LargeGOT is being used. if (!TM.isPositionIndependent() || !TM.getABI().IsO32() || LargeGOT) UseFastISel = false; return UseFastISel ? Mips::createFastISel(funcInfo, libInfo) : nullptr; } EVT MipsTargetLowering::getSetCCResultType(const DataLayout &, LLVMContext &, EVT VT) const { if (!VT.isVector()) return MVT::i32; return VT.changeVectorElementTypeToInteger(); } static SDValue performDivRemCombine(SDNode *N, SelectionDAG &DAG, TargetLowering::DAGCombinerInfo &DCI, const MipsSubtarget &Subtarget) { if (DCI.isBeforeLegalizeOps()) return SDValue(); EVT Ty = N->getValueType(0); unsigned LO = (Ty == MVT::i32) ? Mips::LO0 : Mips::LO0_64; unsigned HI = (Ty == MVT::i32) ? Mips::HI0 : Mips::HI0_64; unsigned Opc = N->getOpcode() == ISD::SDIVREM ? MipsISD::DivRem16 : MipsISD::DivRemU16; SDLoc DL(N); SDValue DivRem = DAG.getNode(Opc, DL, MVT::Glue, N->getOperand(0), N->getOperand(1)); SDValue InChain = DAG.getEntryNode(); SDValue InGlue = DivRem; // insert MFLO if (N->hasAnyUseOfValue(0)) { SDValue CopyFromLo = DAG.getCopyFromReg(InChain, DL, LO, Ty, InGlue); DAG.ReplaceAllUsesOfValueWith(SDValue(N, 0), CopyFromLo); InChain = CopyFromLo.getValue(1); InGlue = CopyFromLo.getValue(2); } // insert MFHI if (N->hasAnyUseOfValue(1)) { SDValue CopyFromHi = DAG.getCopyFromReg(InChain, DL, HI, Ty, InGlue); DAG.ReplaceAllUsesOfValueWith(SDValue(N, 1), CopyFromHi); } return SDValue(); } static Mips::CondCode condCodeToFCC(ISD::CondCode CC) { switch (CC) { default: llvm_unreachable("Unknown fp condition code!"); case ISD::SETEQ: case ISD::SETOEQ: return Mips::FCOND_OEQ; case ISD::SETUNE: return Mips::FCOND_UNE; case ISD::SETLT: case ISD::SETOLT: return Mips::FCOND_OLT; case ISD::SETGT: case ISD::SETOGT: return Mips::FCOND_OGT; case ISD::SETLE: case ISD::SETOLE: return Mips::FCOND_OLE; case ISD::SETGE: case ISD::SETOGE: return Mips::FCOND_OGE; case ISD::SETULT: return Mips::FCOND_ULT; case ISD::SETULE: return Mips::FCOND_ULE; case ISD::SETUGT: return Mips::FCOND_UGT; case ISD::SETUGE: return Mips::FCOND_UGE; case ISD::SETUO: return Mips::FCOND_UN; case ISD::SETO: return Mips::FCOND_OR; case ISD::SETNE: case ISD::SETONE: return Mips::FCOND_ONE; case ISD::SETUEQ: return Mips::FCOND_UEQ; } } /// This function returns true if the floating point conditional branches and /// conditional moves which use condition code CC should be inverted. static bool invertFPCondCodeUser(Mips::CondCode CC) { if (CC >= Mips::FCOND_F && CC <= Mips::FCOND_NGT) return false; assert((CC >= Mips::FCOND_T && CC <= Mips::FCOND_GT) && "Illegal Condition Code"); return true; } // Creates and returns an FPCmp node from a setcc node. // Returns Op if setcc is not a floating point comparison. static SDValue createFPCmp(SelectionDAG &DAG, const SDValue &Op) { // must be a SETCC node if (Op.getOpcode() != ISD::SETCC) return Op; SDValue LHS = Op.getOperand(0); if (!LHS.getValueType().isFloatingPoint()) return Op; SDValue RHS = Op.getOperand(1); SDLoc DL(Op); // Assume the 3rd operand is a CondCodeSDNode. Add code to check the type of // node if necessary. ISD::CondCode CC = cast(Op.getOperand(2))->get(); return DAG.getNode(MipsISD::FPCmp, DL, MVT::Glue, LHS, RHS, DAG.getConstant(condCodeToFCC(CC), DL, MVT::i32)); } // Creates and returns a CMovFPT/F node. static SDValue createCMovFP(SelectionDAG &DAG, SDValue Cond, SDValue True, SDValue False, const SDLoc &DL) { ConstantSDNode *CC = cast(Cond.getOperand(2)); bool invert = invertFPCondCodeUser((Mips::CondCode)CC->getSExtValue()); SDValue FCC0 = DAG.getRegister(Mips::FCC0, MVT::i32); return DAG.getNode((invert ? MipsISD::CMovFP_F : MipsISD::CMovFP_T), DL, True.getValueType(), True, FCC0, False, Cond); } static SDValue performSELECTCombine(SDNode *N, SelectionDAG &DAG, TargetLowering::DAGCombinerInfo &DCI, const MipsSubtarget &Subtarget) { if (DCI.isBeforeLegalizeOps()) return SDValue(); SDValue SetCC = N->getOperand(0); if ((SetCC.getOpcode() != ISD::SETCC) || !SetCC.getOperand(0).getValueType().isInteger()) return SDValue(); SDValue False = N->getOperand(2); EVT FalseTy = False.getValueType(); if (!FalseTy.isInteger()) return SDValue(); ConstantSDNode *FalseC = dyn_cast(False); // If the RHS (False) is 0, we swap the order of the operands // of ISD::SELECT (obviously also inverting the condition) so that we can // take advantage of conditional moves using the $0 register. // Example: // return (a != 0) ? x : 0; // load $reg, x // movz $reg, $0, a if (!FalseC) return SDValue(); const SDLoc DL(N); if (!FalseC->getZExtValue()) { ISD::CondCode CC = cast(SetCC.getOperand(2))->get(); SDValue True = N->getOperand(1); SetCC = DAG.getSetCC(DL, SetCC.getValueType(), SetCC.getOperand(0), SetCC.getOperand(1), ISD::getSetCCInverse(CC, true)); return DAG.getNode(ISD::SELECT, DL, FalseTy, SetCC, False, True); } // If both operands are integer constants there's a possibility that we // can do some interesting optimizations. SDValue True = N->getOperand(1); ConstantSDNode *TrueC = dyn_cast(True); if (!TrueC || !True.getValueType().isInteger()) return SDValue(); // We'll also ignore MVT::i64 operands as this optimizations proves // to be ineffective because of the required sign extensions as the result // of a SETCC operator is always MVT::i32 for non-vector types. if (True.getValueType() == MVT::i64) return SDValue(); int64_t Diff = TrueC->getSExtValue() - FalseC->getSExtValue(); // 1) (a < x) ? y : y-1 // slti $reg1, a, x // addiu $reg2, $reg1, y-1 if (Diff == 1) return DAG.getNode(ISD::ADD, DL, SetCC.getValueType(), SetCC, False); // 2) (a < x) ? y-1 : y // slti $reg1, a, x // xor $reg1, $reg1, 1 // addiu $reg2, $reg1, y-1 if (Diff == -1) { ISD::CondCode CC = cast(SetCC.getOperand(2))->get(); SetCC = DAG.getSetCC(DL, SetCC.getValueType(), SetCC.getOperand(0), SetCC.getOperand(1), ISD::getSetCCInverse(CC, true)); return DAG.getNode(ISD::ADD, DL, SetCC.getValueType(), SetCC, True); } // Could not optimize. return SDValue(); } static SDValue performCMovFPCombine(SDNode *N, SelectionDAG &DAG, TargetLowering::DAGCombinerInfo &DCI, const MipsSubtarget &Subtarget) { if (DCI.isBeforeLegalizeOps()) return SDValue(); SDValue ValueIfTrue = N->getOperand(0), ValueIfFalse = N->getOperand(2); ConstantSDNode *FalseC = dyn_cast(ValueIfFalse); if (!FalseC || FalseC->getZExtValue()) return SDValue(); // Since RHS (False) is 0, we swap the order of the True/False operands // (obviously also inverting the condition) so that we can // take advantage of conditional moves using the $0 register. // Example: // return (a != 0) ? x : 0; // load $reg, x // movz $reg, $0, a unsigned Opc = (N->getOpcode() == MipsISD::CMovFP_T) ? MipsISD::CMovFP_F : MipsISD::CMovFP_T; SDValue FCC = N->getOperand(1), Glue = N->getOperand(3); return DAG.getNode(Opc, SDLoc(N), ValueIfFalse.getValueType(), ValueIfFalse, FCC, ValueIfTrue, Glue); } static SDValue performANDCombine(SDNode *N, SelectionDAG &DAG, TargetLowering::DAGCombinerInfo &DCI, const MipsSubtarget &Subtarget) { if (DCI.isBeforeLegalizeOps() || !Subtarget.hasExtractInsert()) return SDValue(); SDValue FirstOperand = N->getOperand(0); unsigned FirstOperandOpc = FirstOperand.getOpcode(); SDValue Mask = N->getOperand(1); EVT ValTy = N->getValueType(0); SDLoc DL(N); uint64_t Pos = 0, SMPos, SMSize; ConstantSDNode *CN; SDValue NewOperand; unsigned Opc; // Op's second operand must be a shifted mask. if (!(CN = dyn_cast(Mask)) || !isShiftedMask(CN->getZExtValue(), SMPos, SMSize)) return SDValue(); if (FirstOperandOpc == ISD::SRA || FirstOperandOpc == ISD::SRL) { // Pattern match EXT. // $dst = and ((sra or srl) $src , pos), (2**size - 1) // => ext $dst, $src, pos, size // The second operand of the shift must be an immediate. if (!(CN = dyn_cast(FirstOperand.getOperand(1)))) return SDValue(); Pos = CN->getZExtValue(); // Return if the shifted mask does not start at bit 0 or the sum of its size // and Pos exceeds the word's size. if (SMPos != 0 || Pos + SMSize > ValTy.getSizeInBits()) return SDValue(); Opc = MipsISD::Ext; NewOperand = FirstOperand.getOperand(0); } else if (FirstOperandOpc == ISD::SHL && Subtarget.hasCnMips()) { // Pattern match CINS. // $dst = and (shl $src , pos), mask // => cins $dst, $src, pos, size // mask is a shifted mask with consecutive 1's, pos = shift amount, // size = population count. // The second operand of the shift must be an immediate. if (!(CN = dyn_cast(FirstOperand.getOperand(1)))) return SDValue(); Pos = CN->getZExtValue(); if (SMPos != Pos || Pos >= ValTy.getSizeInBits() || SMSize >= 32 || Pos + SMSize > ValTy.getSizeInBits()) return SDValue(); NewOperand = FirstOperand.getOperand(0); // SMSize is 'location' (position) in this case, not size. SMSize--; Opc = MipsISD::CIns; } else { // Pattern match EXT. // $dst = and $src, (2**size - 1) , if size > 16 // => ext $dst, $src, pos, size , pos = 0 // If the mask is <= 0xffff, andi can be used instead. if (CN->getZExtValue() <= 0xffff) return SDValue(); // Return if the mask doesn't start at position 0. if (SMPos) return SDValue(); Opc = MipsISD::Ext; NewOperand = FirstOperand; } return DAG.getNode(Opc, DL, ValTy, NewOperand, DAG.getConstant(Pos, DL, MVT::i32), DAG.getConstant(SMSize, DL, MVT::i32)); } static SDValue performORCombine(SDNode *N, SelectionDAG &DAG, TargetLowering::DAGCombinerInfo &DCI, const MipsSubtarget &Subtarget) { // Pattern match INS. // $dst = or (and $src1 , mask0), (and (shl $src, pos), mask1), // where mask1 = (2**size - 1) << pos, mask0 = ~mask1 // => ins $dst, $src, size, pos, $src1 if (DCI.isBeforeLegalizeOps() || !Subtarget.hasExtractInsert()) return SDValue(); SDValue And0 = N->getOperand(0), And1 = N->getOperand(1); uint64_t SMPos0, SMSize0, SMPos1, SMSize1; ConstantSDNode *CN, *CN1; // See if Op's first operand matches (and $src1 , mask0). if (And0.getOpcode() != ISD::AND) return SDValue(); if (!(CN = dyn_cast(And0.getOperand(1))) || !isShiftedMask(~CN->getSExtValue(), SMPos0, SMSize0)) return SDValue(); // See if Op's second operand matches (and (shl $src, pos), mask1). if (And1.getOpcode() == ISD::AND && And1.getOperand(0).getOpcode() == ISD::SHL) { if (!(CN = dyn_cast(And1.getOperand(1))) || !isShiftedMask(CN->getZExtValue(), SMPos1, SMSize1)) return SDValue(); // The shift masks must have the same position and size. if (SMPos0 != SMPos1 || SMSize0 != SMSize1) return SDValue(); SDValue Shl = And1.getOperand(0); if (!(CN = dyn_cast(Shl.getOperand(1)))) return SDValue(); unsigned Shamt = CN->getZExtValue(); // Return if the shift amount and the first bit position of mask are not the // same. EVT ValTy = N->getValueType(0); if ((Shamt != SMPos0) || (SMPos0 + SMSize0 > ValTy.getSizeInBits())) return SDValue(); SDLoc DL(N); return DAG.getNode(MipsISD::Ins, DL, ValTy, Shl.getOperand(0), DAG.getConstant(SMPos0, DL, MVT::i32), DAG.getConstant(SMSize0, DL, MVT::i32), And0.getOperand(0)); } else { // Pattern match DINS. // $dst = or (and $src, mask0), mask1 // where mask0 = ((1 << SMSize0) -1) << SMPos0 // => dins $dst, $src, pos, size if (~CN->getSExtValue() == ((((int64_t)1 << SMSize0) - 1) << SMPos0) && ((SMSize0 + SMPos0 <= 64 && Subtarget.hasMips64r2()) || (SMSize0 + SMPos0 <= 32))) { // Check if AND instruction has constant as argument bool isConstCase = And1.getOpcode() != ISD::AND; if (And1.getOpcode() == ISD::AND) { if (!(CN1 = dyn_cast(And1->getOperand(1)))) return SDValue(); } else { if (!(CN1 = dyn_cast(N->getOperand(1)))) return SDValue(); } // Don't generate INS if constant OR operand doesn't fit into bits // cleared by constant AND operand. if (CN->getSExtValue() & CN1->getSExtValue()) return SDValue(); SDLoc DL(N); EVT ValTy = N->getOperand(0)->getValueType(0); SDValue Const1; SDValue SrlX; if (!isConstCase) { Const1 = DAG.getConstant(SMPos0, DL, MVT::i32); SrlX = DAG.getNode(ISD::SRL, DL, And1->getValueType(0), And1, Const1); } return DAG.getNode( MipsISD::Ins, DL, N->getValueType(0), isConstCase ? DAG.getConstant(CN1->getSExtValue() >> SMPos0, DL, ValTy) : SrlX, DAG.getConstant(SMPos0, DL, MVT::i32), DAG.getConstant(ValTy.getSizeInBits() / 8 < 8 ? SMSize0 & 31 : SMSize0, DL, MVT::i32), And0->getOperand(0)); } return SDValue(); } } static SDValue performMADD_MSUBCombine(SDNode *ROOTNode, SelectionDAG &CurDAG, const MipsSubtarget &Subtarget) { // ROOTNode must have a multiplication as an operand for the match to be // successful. if (ROOTNode->getOperand(0).getOpcode() != ISD::MUL && ROOTNode->getOperand(1).getOpcode() != ISD::MUL) return SDValue(); // We don't handle vector types here. if (ROOTNode->getValueType(0).isVector()) return SDValue(); // For MIPS64, madd / msub instructions are inefficent to use with 64 bit // arithmetic. E.g. // (add (mul a b) c) => // let res = (madd (mthi (drotr c 32))x(mtlo c) a b) in // MIPS64: (or (dsll (mfhi res) 32) (dsrl (dsll (mflo res) 32) 32) // or // MIPS64R2: (dins (mflo res) (mfhi res) 32 32) // // The overhead of setting up the Hi/Lo registers and reassembling the // result makes this a dubious optimzation for MIPS64. The core of the // problem is that Hi/Lo contain the upper and lower 32 bits of the // operand and result. // // It requires a chain of 4 add/mul for MIPS64R2 to get better code // density than doing it naively, 5 for MIPS64. Additionally, using // madd/msub on MIPS64 requires the operands actually be 32 bit sign // extended operands, not true 64 bit values. // // FIXME: For the moment, disable this completely for MIPS64. if (Subtarget.hasMips64()) return SDValue(); SDValue Mult = ROOTNode->getOperand(0).getOpcode() == ISD::MUL ? ROOTNode->getOperand(0) : ROOTNode->getOperand(1); SDValue AddOperand = ROOTNode->getOperand(0).getOpcode() == ISD::MUL ? ROOTNode->getOperand(1) : ROOTNode->getOperand(0); // Transform this to a MADD only if the user of this node is the add. // If there are other users of the mul, this function returns here. if (!Mult.hasOneUse()) return SDValue(); // maddu and madd are unusual instructions in that on MIPS64 bits 63..31 // must be in canonical form, i.e. sign extended. For MIPS32, the operands // of the multiply must have 32 or more sign bits, otherwise we cannot // perform this optimization. We have to check this here as we're performing // this optimization pre-legalization. SDValue MultLHS = Mult->getOperand(0); SDValue MultRHS = Mult->getOperand(1); bool IsSigned = MultLHS->getOpcode() == ISD::SIGN_EXTEND && MultRHS->getOpcode() == ISD::SIGN_EXTEND; bool IsUnsigned = MultLHS->getOpcode() == ISD::ZERO_EXTEND && MultRHS->getOpcode() == ISD::ZERO_EXTEND; if (!IsSigned && !IsUnsigned) return SDValue(); // Initialize accumulator. SDLoc DL(ROOTNode); SDValue TopHalf; SDValue BottomHalf; BottomHalf = CurDAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i32, AddOperand, CurDAG.getIntPtrConstant(0, DL)); TopHalf = CurDAG.getNode(ISD::EXTRACT_ELEMENT, DL, MVT::i32, AddOperand, CurDAG.getIntPtrConstant(1, DL)); SDValue ACCIn = CurDAG.getNode(MipsISD::MTLOHI, DL, MVT::Untyped, BottomHalf, TopHalf); // Create MipsMAdd(u) / MipsMSub(u) node. bool IsAdd = ROOTNode->getOpcode() == ISD::ADD; unsigned Opcode = IsAdd ? (IsUnsigned ? MipsISD::MAddu : MipsISD::MAdd) : (IsUnsigned ? MipsISD::MSubu : MipsISD::MSub); SDValue MAddOps[3] = { CurDAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Mult->getOperand(0)), CurDAG.getNode(ISD::TRUNCATE, DL, MVT::i32, Mult->getOperand(1)), ACCIn}; EVT VTs[2] = {MVT::i32, MVT::i32}; SDValue MAdd = CurDAG.getNode(Opcode, DL, VTs, MAddOps); SDValue ResLo = CurDAG.getNode(MipsISD::MFLO, DL, MVT::i32, MAdd); SDValue ResHi = CurDAG.getNode(MipsISD::MFHI, DL, MVT::i32, MAdd); SDValue Combined = CurDAG.getNode(ISD::BUILD_PAIR, DL, MVT::i64, ResLo, ResHi); return Combined; } static SDValue performSUBCombine(SDNode *N, SelectionDAG &DAG, TargetLowering::DAGCombinerInfo &DCI, const MipsSubtarget &Subtarget) { // (sub v0 (mul v1, v2)) => (msub v1, v2, v0) if (DCI.isBeforeLegalizeOps()) { if (Subtarget.hasMips32() && !Subtarget.hasMips32r6() && !Subtarget.inMips16Mode() && N->getValueType(0) == MVT::i64) return performMADD_MSUBCombine(N, DAG, Subtarget); return SDValue(); } return SDValue(); } static SDValue performADDCombine(SDNode *N, SelectionDAG &DAG, TargetLowering::DAGCombinerInfo &DCI, const MipsSubtarget &Subtarget) { // (add v0 (mul v1, v2)) => (madd v1, v2, v0) if (DCI.isBeforeLegalizeOps()) { if (Subtarget.hasMips32() && !Subtarget.hasMips32r6() && !Subtarget.inMips16Mode() && N->getValueType(0) == MVT::i64) return performMADD_MSUBCombine(N, DAG, Subtarget); return SDValue(); } // (add v0, (add v1, abs_lo(tjt))) => (add (add v0, v1), abs_lo(tjt)) SDValue Add = N->getOperand(1); if (Add.getOpcode() != ISD::ADD) return SDValue(); SDValue Lo = Add.getOperand(1); if ((Lo.getOpcode() != MipsISD::Lo) || (Lo.getOperand(0).getOpcode() != ISD::TargetJumpTable)) return SDValue(); EVT ValTy = N->getValueType(0); SDLoc DL(N); SDValue Add1 = DAG.getNode(ISD::ADD, DL, ValTy, N->getOperand(0), Add.getOperand(0)); return DAG.getNode(ISD::ADD, DL, ValTy, Add1, Lo); } static SDValue performSHLCombine(SDNode *N, SelectionDAG &DAG, TargetLowering::DAGCombinerInfo &DCI, const MipsSubtarget &Subtarget) { // Pattern match CINS. // $dst = shl (and $src , imm), pos // => cins $dst, $src, pos, size if (DCI.isBeforeLegalizeOps() || !Subtarget.hasCnMips()) return SDValue(); SDValue FirstOperand = N->getOperand(0); unsigned FirstOperandOpc = FirstOperand.getOpcode(); SDValue SecondOperand = N->getOperand(1); EVT ValTy = N->getValueType(0); SDLoc DL(N); uint64_t Pos = 0, SMPos, SMSize; ConstantSDNode *CN; SDValue NewOperand; // The second operand of the shift must be an immediate. if (!(CN = dyn_cast(SecondOperand))) return SDValue(); Pos = CN->getZExtValue(); if (Pos >= ValTy.getSizeInBits()) return SDValue(); if (FirstOperandOpc != ISD::AND) return SDValue(); // AND's second operand must be a shifted mask. if (!(CN = dyn_cast(FirstOperand.getOperand(1))) || !isShiftedMask(CN->getZExtValue(), SMPos, SMSize)) return SDValue(); // Return if the shifted mask does not start at bit 0 or the sum of its size // and Pos exceeds the word's size. if (SMPos != 0 || SMSize > 32 || Pos + SMSize > ValTy.getSizeInBits()) return SDValue(); NewOperand = FirstOperand.getOperand(0); // SMSize is 'location' (position) in this case, not size. SMSize--; return DAG.getNode(MipsISD::CIns, DL, ValTy, NewOperand, DAG.getConstant(Pos, DL, MVT::i32), DAG.getConstant(SMSize, DL, MVT::i32)); } SDValue MipsTargetLowering::PerformDAGCombine(SDNode *N, DAGCombinerInfo &DCI) const { SelectionDAG &DAG = DCI.DAG; unsigned Opc = N->getOpcode(); switch (Opc) { default: break; case ISD::SDIVREM: case ISD::UDIVREM: return performDivRemCombine(N, DAG, DCI, Subtarget); case ISD::SELECT: return performSELECTCombine(N, DAG, DCI, Subtarget); case MipsISD::CMovFP_F: case MipsISD::CMovFP_T: return performCMovFPCombine(N, DAG, DCI, Subtarget); case ISD::AND: return performANDCombine(N, DAG, DCI, Subtarget); case ISD::OR: return performORCombine(N, DAG, DCI, Subtarget); case ISD::ADD: return performADDCombine(N, DAG, DCI, Subtarget); case ISD::SHL: return performSHLCombine(N, DAG, DCI, Subtarget); case ISD::SUB: return performSUBCombine(N, DAG, DCI, Subtarget); } return SDValue(); } bool MipsTargetLowering::isCheapToSpeculateCttz() const { return Subtarget.hasMips32(); } bool MipsTargetLowering::isCheapToSpeculateCtlz() const { return Subtarget.hasMips32(); } void MipsTargetLowering::LowerOperationWrapper(SDNode *N, SmallVectorImpl &Results, SelectionDAG &DAG) const { SDValue Res = LowerOperation(SDValue(N, 0), DAG); for (unsigned I = 0, E = Res->getNumValues(); I != E; ++I) Results.push_back(Res.getValue(I)); } void MipsTargetLowering::ReplaceNodeResults(SDNode *N, SmallVectorImpl &Results, SelectionDAG &DAG) const { return LowerOperationWrapper(N, Results, DAG); } SDValue MipsTargetLowering:: LowerOperation(SDValue Op, SelectionDAG &DAG) const { switch (Op.getOpcode()) { case ISD::BRCOND: return lowerBRCOND(Op, DAG); case ISD::ConstantPool: return lowerConstantPool(Op, DAG); case ISD::GlobalAddress: return lowerGlobalAddress(Op, DAG); case ISD::BlockAddress: return lowerBlockAddress(Op, DAG); case ISD::GlobalTLSAddress: return lowerGlobalTLSAddress(Op, DAG); case ISD::JumpTable: return lowerJumpTable(Op, DAG); case ISD::SELECT: return lowerSELECT(Op, DAG); case ISD::SETCC: return lowerSETCC(Op, DAG); case ISD::VASTART: return lowerVASTART(Op, DAG); case ISD::VAARG: return lowerVAARG(Op, DAG); case ISD::FCOPYSIGN: return lowerFCOPYSIGN(Op, DAG); case ISD::FRAMEADDR: return lowerFRAMEADDR(Op, DAG); case ISD::RETURNADDR: return lowerRETURNADDR(Op, DAG); case ISD::EH_RETURN: return lowerEH_RETURN(Op, DAG); case ISD::ATOMIC_FENCE: return lowerATOMIC_FENCE(Op, DAG); case ISD::SHL_PARTS: return lowerShiftLeftParts(Op, DAG); case ISD::SRA_PARTS: return lowerShiftRightParts(Op, DAG, true); case ISD::SRL_PARTS: return lowerShiftRightParts(Op, DAG, false); case ISD::LOAD: return lowerLOAD(Op, DAG); case ISD::STORE: return lowerSTORE(Op, DAG); case ISD::EH_DWARF_CFA: return lowerEH_DWARF_CFA(Op, DAG); case ISD::FP_TO_SINT: return lowerFP_TO_SINT(Op, DAG); } return SDValue(); } //===----------------------------------------------------------------------===// // Lower helper functions //===----------------------------------------------------------------------===// // addLiveIn - This helper function adds the specified physical register to the // MachineFunction as a live in value. It also creates a corresponding // virtual register for it. static unsigned addLiveIn(MachineFunction &MF, unsigned PReg, const TargetRegisterClass *RC) { unsigned VReg = MF.getRegInfo().createVirtualRegister(RC); MF.getRegInfo().addLiveIn(PReg, VReg); return VReg; } static MachineBasicBlock *insertDivByZeroTrap(MachineInstr &MI, MachineBasicBlock &MBB, const TargetInstrInfo &TII, bool Is64Bit, bool IsMicroMips) { if (NoZeroDivCheck) return &MBB; // Insert instruction "teq $divisor_reg, $zero, 7". MachineBasicBlock::iterator I(MI); MachineInstrBuilder MIB; MachineOperand &Divisor = MI.getOperand(2); MIB = BuildMI(MBB, std::next(I), MI.getDebugLoc(), TII.get(IsMicroMips ? Mips::TEQ_MM : Mips::TEQ)) .addReg(Divisor.getReg(), getKillRegState(Divisor.isKill())) .addReg(Mips::ZERO) .addImm(7); // Use the 32-bit sub-register if this is a 64-bit division. if (Is64Bit) MIB->getOperand(0).setSubReg(Mips::sub_32); // Clear Divisor's kill flag. Divisor.setIsKill(false); // We would normally delete the original instruction here but in this case // we only needed to inject an additional instruction rather than replace it. return &MBB; } MachineBasicBlock * MipsTargetLowering::EmitInstrWithCustomInserter(MachineInstr &MI, MachineBasicBlock *BB) const { switch (MI.getOpcode()) { default: llvm_unreachable("Unexpected instr type to insert"); case Mips::ATOMIC_LOAD_ADD_I8: - return emitAtomicBinaryPartword(MI, BB, 1, Mips::ADDu); + return emitAtomicBinaryPartword(MI, BB, 1); case Mips::ATOMIC_LOAD_ADD_I16: - return emitAtomicBinaryPartword(MI, BB, 2, Mips::ADDu); + return emitAtomicBinaryPartword(MI, BB, 2); case Mips::ATOMIC_LOAD_ADD_I32: - return emitAtomicBinary(MI, BB, 4, Mips::ADDu); + return emitAtomicBinary(MI, BB); case Mips::ATOMIC_LOAD_ADD_I64: - return emitAtomicBinary(MI, BB, 8, Mips::DADDu); + return emitAtomicBinary(MI, BB); case Mips::ATOMIC_LOAD_AND_I8: - return emitAtomicBinaryPartword(MI, BB, 1, Mips::AND); + return emitAtomicBinaryPartword(MI, BB, 1); case Mips::ATOMIC_LOAD_AND_I16: - return emitAtomicBinaryPartword(MI, BB, 2, Mips::AND); + return emitAtomicBinaryPartword(MI, BB, 2); case Mips::ATOMIC_LOAD_AND_I32: - return emitAtomicBinary(MI, BB, 4, Mips::AND); + return emitAtomicBinary(MI, BB); case Mips::ATOMIC_LOAD_AND_I64: - return emitAtomicBinary(MI, BB, 8, Mips::AND64); + return emitAtomicBinary(MI, BB); case Mips::ATOMIC_LOAD_OR_I8: - return emitAtomicBinaryPartword(MI, BB, 1, Mips::OR); + return emitAtomicBinaryPartword(MI, BB, 1); case Mips::ATOMIC_LOAD_OR_I16: - return emitAtomicBinaryPartword(MI, BB, 2, Mips::OR); + return emitAtomicBinaryPartword(MI, BB, 2); case Mips::ATOMIC_LOAD_OR_I32: - return emitAtomicBinary(MI, BB, 4, Mips::OR); + return emitAtomicBinary(MI, BB); case Mips::ATOMIC_LOAD_OR_I64: - return emitAtomicBinary(MI, BB, 8, Mips::OR64); + return emitAtomicBinary(MI, BB); case Mips::ATOMIC_LOAD_XOR_I8: - return emitAtomicBinaryPartword(MI, BB, 1, Mips::XOR); + return emitAtomicBinaryPartword(MI, BB, 1); case Mips::ATOMIC_LOAD_XOR_I16: - return emitAtomicBinaryPartword(MI, BB, 2, Mips::XOR); + return emitAtomicBinaryPartword(MI, BB, 2); case Mips::ATOMIC_LOAD_XOR_I32: - return emitAtomicBinary(MI, BB, 4, Mips::XOR); + return emitAtomicBinary(MI, BB); case Mips::ATOMIC_LOAD_XOR_I64: - return emitAtomicBinary(MI, BB, 8, Mips::XOR64); + return emitAtomicBinary(MI, BB); case Mips::ATOMIC_LOAD_NAND_I8: - return emitAtomicBinaryPartword(MI, BB, 1, 0, true); + return emitAtomicBinaryPartword(MI, BB, 1); case Mips::ATOMIC_LOAD_NAND_I16: - return emitAtomicBinaryPartword(MI, BB, 2, 0, true); + return emitAtomicBinaryPartword(MI, BB, 2); case Mips::ATOMIC_LOAD_NAND_I32: - return emitAtomicBinary(MI, BB, 4, 0, true); + return emitAtomicBinary(MI, BB); case Mips::ATOMIC_LOAD_NAND_I64: - return emitAtomicBinary(MI, BB, 8, 0, true); + return emitAtomicBinary(MI, BB); case Mips::ATOMIC_LOAD_SUB_I8: - return emitAtomicBinaryPartword(MI, BB, 1, Mips::SUBu); + return emitAtomicBinaryPartword(MI, BB, 1); case Mips::ATOMIC_LOAD_SUB_I16: - return emitAtomicBinaryPartword(MI, BB, 2, Mips::SUBu); + return emitAtomicBinaryPartword(MI, BB, 2); case Mips::ATOMIC_LOAD_SUB_I32: - return emitAtomicBinary(MI, BB, 4, Mips::SUBu); + return emitAtomicBinary(MI, BB); case Mips::ATOMIC_LOAD_SUB_I64: - return emitAtomicBinary(MI, BB, 8, Mips::DSUBu); + return emitAtomicBinary(MI, BB); case Mips::ATOMIC_SWAP_I8: - return emitAtomicBinaryPartword(MI, BB, 1, 0); + return emitAtomicBinaryPartword(MI, BB, 1); case Mips::ATOMIC_SWAP_I16: - return emitAtomicBinaryPartword(MI, BB, 2, 0); + return emitAtomicBinaryPartword(MI, BB, 2); case Mips::ATOMIC_SWAP_I32: - return emitAtomicBinary(MI, BB, 4, 0); + return emitAtomicBinary(MI, BB); case Mips::ATOMIC_SWAP_I64: - return emitAtomicBinary(MI, BB, 8, 0); + return emitAtomicBinary(MI, BB); case Mips::ATOMIC_CMP_SWAP_I8: return emitAtomicCmpSwapPartword(MI, BB, 1); case Mips::ATOMIC_CMP_SWAP_I16: return emitAtomicCmpSwapPartword(MI, BB, 2); case Mips::ATOMIC_CMP_SWAP_I32: - return emitAtomicCmpSwap(MI, BB, 4); + return emitAtomicCmpSwap(MI, BB); case Mips::ATOMIC_CMP_SWAP_I64: - return emitAtomicCmpSwap(MI, BB, 8); + return emitAtomicCmpSwap(MI, BB); case Mips::PseudoSDIV: case Mips::PseudoUDIV: case Mips::DIV: case Mips::DIVU: case Mips::MOD: case Mips::MODU: return insertDivByZeroTrap(MI, *BB, *Subtarget.getInstrInfo(), false, false); case Mips::SDIV_MM_Pseudo: case Mips::UDIV_MM_Pseudo: case Mips::SDIV_MM: case Mips::UDIV_MM: case Mips::DIV_MMR6: case Mips::DIVU_MMR6: case Mips::MOD_MMR6: case Mips::MODU_MMR6: return insertDivByZeroTrap(MI, *BB, *Subtarget.getInstrInfo(), false, true); case Mips::PseudoDSDIV: case Mips::PseudoDUDIV: case Mips::DDIV: case Mips::DDIVU: case Mips::DMOD: case Mips::DMODU: return insertDivByZeroTrap(MI, *BB, *Subtarget.getInstrInfo(), true, false); case Mips::PseudoSELECT_I: case Mips::PseudoSELECT_I64: case Mips::PseudoSELECT_S: case Mips::PseudoSELECT_D32: case Mips::PseudoSELECT_D64: return emitPseudoSELECT(MI, BB, false, Mips::BNE); case Mips::PseudoSELECTFP_F_I: case Mips::PseudoSELECTFP_F_I64: case Mips::PseudoSELECTFP_F_S: case Mips::PseudoSELECTFP_F_D32: case Mips::PseudoSELECTFP_F_D64: return emitPseudoSELECT(MI, BB, true, Mips::BC1F); case Mips::PseudoSELECTFP_T_I: case Mips::PseudoSELECTFP_T_I64: case Mips::PseudoSELECTFP_T_S: case Mips::PseudoSELECTFP_T_D32: case Mips::PseudoSELECTFP_T_D64: return emitPseudoSELECT(MI, BB, true, Mips::BC1T); } } // This function also handles Mips::ATOMIC_SWAP_I32 (when BinOpcode == 0), and // Mips::ATOMIC_LOAD_NAND_I32 (when Nand == true) -MachineBasicBlock *MipsTargetLowering::emitAtomicBinary(MachineInstr &MI, - MachineBasicBlock *BB, - unsigned Size, - unsigned BinOpcode, - bool Nand) const { - assert((Size == 4 || Size == 8) && "Unsupported size for EmitAtomicBinary."); +MachineBasicBlock * +MipsTargetLowering::emitAtomicBinary(MachineInstr &MI, + MachineBasicBlock *BB) const { MachineFunction *MF = BB->getParent(); MachineRegisterInfo &RegInfo = MF->getRegInfo(); - const TargetRegisterClass *RC = getRegClassFor(MVT::getIntegerVT(Size * 8)); const TargetInstrInfo *TII = Subtarget.getInstrInfo(); - const bool ArePtrs64bit = ABI.ArePtrs64bit(); DebugLoc DL = MI.getDebugLoc(); - unsigned LL, SC, AND, NOR, ZERO, BEQ; - if (Size == 4) { - if (isMicroMips) { - LL = Subtarget.hasMips32r6() ? Mips::LL_MMR6 : Mips::LL_MM; - SC = Subtarget.hasMips32r6() ? Mips::SC_MMR6 : Mips::SC_MM; - } else { - LL = Subtarget.hasMips32r6() - ? (ArePtrs64bit ? Mips::LL64_R6 : Mips::LL_R6) - : (ArePtrs64bit ? Mips::LL64 : Mips::LL); - SC = Subtarget.hasMips32r6() - ? (ArePtrs64bit ? Mips::SC64_R6 : Mips::SC_R6) - : (ArePtrs64bit ? Mips::SC64 : Mips::SC); - } - - AND = Mips::AND; - NOR = Mips::NOR; - ZERO = Mips::ZERO; - BEQ = Mips::BEQ; - } else { - LL = Subtarget.hasMips64r6() ? Mips::LLD_R6 : Mips::LLD; - SC = Subtarget.hasMips64r6() ? Mips::SCD_R6 : Mips::SCD; - AND = Mips::AND64; - NOR = Mips::NOR64; - ZERO = Mips::ZERO_64; - BEQ = Mips::BEQ64; + unsigned AtomicOp; + switch (MI.getOpcode()) { + case Mips::ATOMIC_LOAD_ADD_I32: + AtomicOp = Mips::ATOMIC_LOAD_ADD_I32_POSTRA; + break; + case Mips::ATOMIC_LOAD_SUB_I32: + AtomicOp = Mips::ATOMIC_LOAD_SUB_I32_POSTRA; + break; + case Mips::ATOMIC_LOAD_AND_I32: + AtomicOp = Mips::ATOMIC_LOAD_AND_I32_POSTRA; + break; + case Mips::ATOMIC_LOAD_OR_I32: + AtomicOp = Mips::ATOMIC_LOAD_OR_I32_POSTRA; + break; + case Mips::ATOMIC_LOAD_XOR_I32: + AtomicOp = Mips::ATOMIC_LOAD_XOR_I32_POSTRA; + break; + case Mips::ATOMIC_LOAD_NAND_I32: + AtomicOp = Mips::ATOMIC_LOAD_NAND_I32_POSTRA; + break; + case Mips::ATOMIC_SWAP_I32: + AtomicOp = Mips::ATOMIC_SWAP_I32_POSTRA; + break; + case Mips::ATOMIC_LOAD_ADD_I64: + AtomicOp = Mips::ATOMIC_LOAD_ADD_I64_POSTRA; + break; + case Mips::ATOMIC_LOAD_SUB_I64: + AtomicOp = Mips::ATOMIC_LOAD_SUB_I64_POSTRA; + break; + case Mips::ATOMIC_LOAD_AND_I64: + AtomicOp = Mips::ATOMIC_LOAD_AND_I64_POSTRA; + break; + case Mips::ATOMIC_LOAD_OR_I64: + AtomicOp = Mips::ATOMIC_LOAD_OR_I64_POSTRA; + break; + case Mips::ATOMIC_LOAD_XOR_I64: + AtomicOp = Mips::ATOMIC_LOAD_XOR_I64_POSTRA; + break; + case Mips::ATOMIC_LOAD_NAND_I64: + AtomicOp = Mips::ATOMIC_LOAD_NAND_I64_POSTRA; + break; + case Mips::ATOMIC_SWAP_I64: + AtomicOp = Mips::ATOMIC_SWAP_I64_POSTRA; + break; + default: + llvm_unreachable("Unknown pseudo atomic for replacement!"); } unsigned OldVal = MI.getOperand(0).getReg(); unsigned Ptr = MI.getOperand(1).getReg(); unsigned Incr = MI.getOperand(2).getReg(); + unsigned Scratch = RegInfo.createVirtualRegister(RegInfo.getRegClass(OldVal)); - unsigned StoreVal = RegInfo.createVirtualRegister(RC); - unsigned AndRes = RegInfo.createVirtualRegister(RC); - unsigned Success = RegInfo.createVirtualRegister(RC); + MachineBasicBlock::iterator II(MI); - // insert new blocks after the current block - const BasicBlock *LLVM_BB = BB->getBasicBlock(); - MachineBasicBlock *loopMBB = MF->CreateMachineBasicBlock(LLVM_BB); - MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); - MachineFunction::iterator It = ++BB->getIterator(); - MF->insert(It, loopMBB); - MF->insert(It, exitMBB); + // The scratch registers here with the EarlyClobber | Define | Implicit + // flags is used to persuade the register allocator and the machine + // verifier to accept the usage of this register. This has to be a real + // register which has an UNDEF value but is dead after the instruction which + // is unique among the registers chosen for the instruction. + + // The EarlyClobber flag has the semantic properties that the operand it is + // attached to is clobbered before the rest of the inputs are read. Hence it + // must be unique among the operands to the instruction. + // The Define flag is needed to coerce the machine verifier that an Undef + // value isn't a problem. + // The Dead flag is needed as the value in scratch isn't used by any other + // instruction. Kill isn't used as Dead is more precise. + // The implicit flag is here due to the interaction between the other flags + // and the machine verifier. + + // For correctness purpose, a new pseudo is introduced here. We need this + // new pseudo, so that FastRegisterAllocator does not see an ll/sc sequence + // that is spread over >1 basic blocks. A register allocator which + // introduces (or any codegen infact) a store, can violate the expectations + // of the hardware. + // + // An atomic read-modify-write sequence starts with a linked load + // instruction and ends with a store conditional instruction. The atomic + // read-modify-write sequence fails if any of the following conditions + // occur between the execution of ll and sc: + // * A coherent store is completed by another process or coherent I/O + // module into the block of synchronizable physical memory containing + // the word. The size and alignment of the block is + // implementation-dependent. + // * A coherent store is executed between an LL and SC sequence on the + // same processor to the block of synchornizable physical memory + // containing the word. + // - // Transfer the remainder of BB and its successor edges to exitMBB. - exitMBB->splice(exitMBB->begin(), BB, - std::next(MachineBasicBlock::iterator(MI)), BB->end()); - exitMBB->transferSuccessorsAndUpdatePHIs(BB); + unsigned PtrCopy = RegInfo.createVirtualRegister(RegInfo.getRegClass(Ptr)); + unsigned IncrCopy = RegInfo.createVirtualRegister(RegInfo.getRegClass(Incr)); - // thisMBB: - // ... - // fallthrough --> loopMBB - BB->addSuccessor(loopMBB); - loopMBB->addSuccessor(loopMBB); - loopMBB->addSuccessor(exitMBB); - - // loopMBB: - // ll oldval, 0(ptr) - // storeval, oldval, incr - // sc success, storeval, 0(ptr) - // beq success, $0, loopMBB - BB = loopMBB; - BuildMI(BB, DL, TII->get(LL), OldVal).addReg(Ptr).addImm(0); - if (Nand) { - // and andres, oldval, incr - // nor storeval, $0, andres - BuildMI(BB, DL, TII->get(AND), AndRes).addReg(OldVal).addReg(Incr); - BuildMI(BB, DL, TII->get(NOR), StoreVal).addReg(ZERO).addReg(AndRes); - } else if (BinOpcode) { - // storeval, oldval, incr - BuildMI(BB, DL, TII->get(BinOpcode), StoreVal).addReg(OldVal).addReg(Incr); - } else { - StoreVal = Incr; - } - BuildMI(BB, DL, TII->get(SC), Success).addReg(StoreVal).addReg(Ptr).addImm(0); - BuildMI(BB, DL, TII->get(BEQ)).addReg(Success).addReg(ZERO).addMBB(loopMBB); + BuildMI(*BB, II, DL, TII->get(Mips::COPY), IncrCopy).addReg(Incr); + BuildMI(*BB, II, DL, TII->get(Mips::COPY), PtrCopy).addReg(Ptr); - MI.eraseFromParent(); // The instruction is gone now. + BuildMI(*BB, II, DL, TII->get(AtomicOp)) + .addReg(OldVal, RegState::Define | RegState::EarlyClobber) + .addReg(PtrCopy) + .addReg(IncrCopy) + .addReg(Scratch, RegState::Define | RegState::EarlyClobber | + RegState::Implicit | RegState::Dead); - return exitMBB; + MI.eraseFromParent(); + + return BB; } MachineBasicBlock *MipsTargetLowering::emitSignExtendToI32InReg( MachineInstr &MI, MachineBasicBlock *BB, unsigned Size, unsigned DstReg, unsigned SrcReg) const { const TargetInstrInfo *TII = Subtarget.getInstrInfo(); const DebugLoc &DL = MI.getDebugLoc(); if (Subtarget.hasMips32r2() && Size == 1) { BuildMI(BB, DL, TII->get(Mips::SEB), DstReg).addReg(SrcReg); return BB; } if (Subtarget.hasMips32r2() && Size == 2) { BuildMI(BB, DL, TII->get(Mips::SEH), DstReg).addReg(SrcReg); return BB; } MachineFunction *MF = BB->getParent(); MachineRegisterInfo &RegInfo = MF->getRegInfo(); const TargetRegisterClass *RC = getRegClassFor(MVT::i32); unsigned ScrReg = RegInfo.createVirtualRegister(RC); assert(Size < 32); int64_t ShiftImm = 32 - (Size * 8); BuildMI(BB, DL, TII->get(Mips::SLL), ScrReg).addReg(SrcReg).addImm(ShiftImm); BuildMI(BB, DL, TII->get(Mips::SRA), DstReg).addReg(ScrReg).addImm(ShiftImm); return BB; } MachineBasicBlock *MipsTargetLowering::emitAtomicBinaryPartword( - MachineInstr &MI, MachineBasicBlock *BB, unsigned Size, unsigned BinOpcode, - bool Nand) const { + MachineInstr &MI, MachineBasicBlock *BB, unsigned Size) const { assert((Size == 1 || Size == 2) && "Unsupported size for EmitAtomicBinaryPartial."); MachineFunction *MF = BB->getParent(); MachineRegisterInfo &RegInfo = MF->getRegInfo(); const TargetRegisterClass *RC = getRegClassFor(MVT::i32); const bool ArePtrs64bit = ABI.ArePtrs64bit(); const TargetRegisterClass *RCp = getRegClassFor(ArePtrs64bit ? MVT::i64 : MVT::i32); const TargetInstrInfo *TII = Subtarget.getInstrInfo(); DebugLoc DL = MI.getDebugLoc(); unsigned Dest = MI.getOperand(0).getReg(); unsigned Ptr = MI.getOperand(1).getReg(); unsigned Incr = MI.getOperand(2).getReg(); unsigned AlignedAddr = RegInfo.createVirtualRegister(RCp); unsigned ShiftAmt = RegInfo.createVirtualRegister(RC); unsigned Mask = RegInfo.createVirtualRegister(RC); unsigned Mask2 = RegInfo.createVirtualRegister(RC); - unsigned NewVal = RegInfo.createVirtualRegister(RC); - unsigned OldVal = RegInfo.createVirtualRegister(RC); unsigned Incr2 = RegInfo.createVirtualRegister(RC); unsigned MaskLSB2 = RegInfo.createVirtualRegister(RCp); unsigned PtrLSB2 = RegInfo.createVirtualRegister(RC); unsigned MaskUpper = RegInfo.createVirtualRegister(RC); - unsigned AndRes = RegInfo.createVirtualRegister(RC); - unsigned BinOpRes = RegInfo.createVirtualRegister(RC); - unsigned MaskedOldVal0 = RegInfo.createVirtualRegister(RC); - unsigned StoreVal = RegInfo.createVirtualRegister(RC); - unsigned MaskedOldVal1 = RegInfo.createVirtualRegister(RC); - unsigned SrlRes = RegInfo.createVirtualRegister(RC); - unsigned Success = RegInfo.createVirtualRegister(RC); - - unsigned LL, SC; - if (isMicroMips) { - LL = Subtarget.hasMips32r6() ? Mips::LL_MMR6 : Mips::LL_MM; - SC = Subtarget.hasMips32r6() ? Mips::SC_MMR6 : Mips::SC_MM; - } else { - LL = Subtarget.hasMips32r6() ? (ArePtrs64bit ? Mips::LL64_R6 : Mips::LL_R6) - : (ArePtrs64bit ? Mips::LL64 : Mips::LL); - SC = Subtarget.hasMips32r6() ? (ArePtrs64bit ? Mips::SC64_R6 : Mips::SC_R6) - : (ArePtrs64bit ? Mips::SC64 : Mips::SC); + unsigned Scratch = RegInfo.createVirtualRegister(RC); + unsigned Scratch2 = RegInfo.createVirtualRegister(RC); + unsigned Scratch3 = RegInfo.createVirtualRegister(RC); + + unsigned AtomicOp = 0; + switch (MI.getOpcode()) { + case Mips::ATOMIC_LOAD_NAND_I8: + AtomicOp = Mips::ATOMIC_LOAD_NAND_I8_POSTRA; + break; + case Mips::ATOMIC_LOAD_NAND_I16: + AtomicOp = Mips::ATOMIC_LOAD_NAND_I16_POSTRA; + break; + case Mips::ATOMIC_SWAP_I8: + AtomicOp = Mips::ATOMIC_SWAP_I8_POSTRA; + break; + case Mips::ATOMIC_SWAP_I16: + AtomicOp = Mips::ATOMIC_SWAP_I16_POSTRA; + break; + case Mips::ATOMIC_LOAD_ADD_I8: + AtomicOp = Mips::ATOMIC_LOAD_ADD_I8_POSTRA; + break; + case Mips::ATOMIC_LOAD_ADD_I16: + AtomicOp = Mips::ATOMIC_LOAD_ADD_I16_POSTRA; + break; + case Mips::ATOMIC_LOAD_SUB_I8: + AtomicOp = Mips::ATOMIC_LOAD_SUB_I8_POSTRA; + break; + case Mips::ATOMIC_LOAD_SUB_I16: + AtomicOp = Mips::ATOMIC_LOAD_SUB_I16_POSTRA; + break; + case Mips::ATOMIC_LOAD_AND_I8: + AtomicOp = Mips::ATOMIC_LOAD_AND_I8_POSTRA; + break; + case Mips::ATOMIC_LOAD_AND_I16: + AtomicOp = Mips::ATOMIC_LOAD_AND_I16_POSTRA; + break; + case Mips::ATOMIC_LOAD_OR_I8: + AtomicOp = Mips::ATOMIC_LOAD_OR_I8_POSTRA; + break; + case Mips::ATOMIC_LOAD_OR_I16: + AtomicOp = Mips::ATOMIC_LOAD_OR_I16_POSTRA; + break; + case Mips::ATOMIC_LOAD_XOR_I8: + AtomicOp = Mips::ATOMIC_LOAD_XOR_I8_POSTRA; + break; + case Mips::ATOMIC_LOAD_XOR_I16: + AtomicOp = Mips::ATOMIC_LOAD_XOR_I16_POSTRA; + break; + default: + llvm_unreachable("Unknown subword atomic pseudo for expansion!"); } // insert new blocks after the current block const BasicBlock *LLVM_BB = BB->getBasicBlock(); - MachineBasicBlock *loopMBB = MF->CreateMachineBasicBlock(LLVM_BB); - MachineBasicBlock *sinkMBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineFunction::iterator It = ++BB->getIterator(); - MF->insert(It, loopMBB); - MF->insert(It, sinkMBB); MF->insert(It, exitMBB); // Transfer the remainder of BB and its successor edges to exitMBB. exitMBB->splice(exitMBB->begin(), BB, std::next(MachineBasicBlock::iterator(MI)), BB->end()); exitMBB->transferSuccessorsAndUpdatePHIs(BB); - BB->addSuccessor(loopMBB); - loopMBB->addSuccessor(loopMBB); - loopMBB->addSuccessor(sinkMBB); - sinkMBB->addSuccessor(exitMBB); + BB->addSuccessor(exitMBB, BranchProbability::getOne()); // thisMBB: // addiu masklsb2,$0,-4 # 0xfffffffc // and alignedaddr,ptr,masklsb2 // andi ptrlsb2,ptr,3 // sll shiftamt,ptrlsb2,3 // ori maskupper,$0,255 # 0xff // sll mask,maskupper,shiftamt // nor mask2,$0,mask // sll incr2,incr,shiftamt int64_t MaskImm = (Size == 1) ? 255 : 65535; BuildMI(BB, DL, TII->get(ABI.GetPtrAddiuOp()), MaskLSB2) .addReg(ABI.GetNullPtr()).addImm(-4); BuildMI(BB, DL, TII->get(ABI.GetPtrAndOp()), AlignedAddr) .addReg(Ptr).addReg(MaskLSB2); BuildMI(BB, DL, TII->get(Mips::ANDi), PtrLSB2) .addReg(Ptr, 0, ArePtrs64bit ? Mips::sub_32 : 0).addImm(3); if (Subtarget.isLittle()) { BuildMI(BB, DL, TII->get(Mips::SLL), ShiftAmt).addReg(PtrLSB2).addImm(3); } else { unsigned Off = RegInfo.createVirtualRegister(RC); BuildMI(BB, DL, TII->get(Mips::XORi), Off) .addReg(PtrLSB2).addImm((Size == 1) ? 3 : 2); BuildMI(BB, DL, TII->get(Mips::SLL), ShiftAmt).addReg(Off).addImm(3); } BuildMI(BB, DL, TII->get(Mips::ORi), MaskUpper) .addReg(Mips::ZERO).addImm(MaskImm); BuildMI(BB, DL, TII->get(Mips::SLLV), Mask) .addReg(MaskUpper).addReg(ShiftAmt); BuildMI(BB, DL, TII->get(Mips::NOR), Mask2).addReg(Mips::ZERO).addReg(Mask); BuildMI(BB, DL, TII->get(Mips::SLLV), Incr2).addReg(Incr).addReg(ShiftAmt); - // atomic.load.binop - // loopMBB: - // ll oldval,0(alignedaddr) - // binop binopres,oldval,incr2 - // and newval,binopres,mask - // and maskedoldval0,oldval,mask2 - // or storeval,maskedoldval0,newval - // sc success,storeval,0(alignedaddr) - // beq success,$0,loopMBB - - // atomic.swap - // loopMBB: - // ll oldval,0(alignedaddr) - // and newval,incr2,mask - // and maskedoldval0,oldval,mask2 - // or storeval,maskedoldval0,newval - // sc success,storeval,0(alignedaddr) - // beq success,$0,loopMBB - - BB = loopMBB; - BuildMI(BB, DL, TII->get(LL), OldVal).addReg(AlignedAddr).addImm(0); - if (Nand) { - // and andres, oldval, incr2 - // nor binopres, $0, andres - // and newval, binopres, mask - BuildMI(BB, DL, TII->get(Mips::AND), AndRes).addReg(OldVal).addReg(Incr2); - BuildMI(BB, DL, TII->get(Mips::NOR), BinOpRes) - .addReg(Mips::ZERO).addReg(AndRes); - BuildMI(BB, DL, TII->get(Mips::AND), NewVal).addReg(BinOpRes).addReg(Mask); - } else if (BinOpcode) { - // binopres, oldval, incr2 - // and newval, binopres, mask - BuildMI(BB, DL, TII->get(BinOpcode), BinOpRes).addReg(OldVal).addReg(Incr2); - BuildMI(BB, DL, TII->get(Mips::AND), NewVal).addReg(BinOpRes).addReg(Mask); - } else { // atomic.swap - // and newval, incr2, mask - BuildMI(BB, DL, TII->get(Mips::AND), NewVal).addReg(Incr2).addReg(Mask); - } - - BuildMI(BB, DL, TII->get(Mips::AND), MaskedOldVal0) - .addReg(OldVal).addReg(Mask2); - BuildMI(BB, DL, TII->get(Mips::OR), StoreVal) - .addReg(MaskedOldVal0).addReg(NewVal); - BuildMI(BB, DL, TII->get(SC), Success) - .addReg(StoreVal).addReg(AlignedAddr).addImm(0); - BuildMI(BB, DL, TII->get(Mips::BEQ)) - .addReg(Success).addReg(Mips::ZERO).addMBB(loopMBB); - // sinkMBB: - // and maskedoldval1,oldval,mask - // srl srlres,maskedoldval1,shiftamt - // sign_extend dest,srlres - BB = sinkMBB; - - BuildMI(BB, DL, TII->get(Mips::AND), MaskedOldVal1) - .addReg(OldVal).addReg(Mask); - BuildMI(BB, DL, TII->get(Mips::SRLV), SrlRes) - .addReg(MaskedOldVal1).addReg(ShiftAmt); - BB = emitSignExtendToI32InReg(MI, BB, Size, Dest, SrlRes); + // The purposes of the flags on the scratch registers is explained in + // emitAtomicBinary. In summary, we need a scratch register which is going to + // be undef, that is unique among registers chosen for the instruction. + + BuildMI(BB, DL, TII->get(AtomicOp)) + .addReg(Dest, RegState::Define | RegState::EarlyClobber) + .addReg(AlignedAddr) + .addReg(Incr2) + .addReg(Mask) + .addReg(Mask2) + .addReg(ShiftAmt) + .addReg(Scratch, RegState::EarlyClobber | RegState::Define | + RegState::Dead | RegState::Implicit) + .addReg(Scratch2, RegState::EarlyClobber | RegState::Define | + RegState::Dead | RegState::Implicit) + .addReg(Scratch3, RegState::EarlyClobber | RegState::Define | + RegState::Dead | RegState::Implicit); MI.eraseFromParent(); // The instruction is gone now. return exitMBB; } -MachineBasicBlock *MipsTargetLowering::emitAtomicCmpSwap(MachineInstr &MI, - MachineBasicBlock *BB, - unsigned Size) const { - assert((Size == 4 || Size == 8) && "Unsupported size for EmitAtomicCmpSwap."); +// Lower atomic compare and swap to a pseudo instruction, taking care to +// define a scratch register for the pseudo instruction's expansion. The +// instruction is expanded after the register allocator as to prevent +// the insertion of stores between the linked load and the store conditional. + +MachineBasicBlock * +MipsTargetLowering::emitAtomicCmpSwap(MachineInstr &MI, + MachineBasicBlock *BB) const { + + assert((MI.getOpcode() == Mips::ATOMIC_CMP_SWAP_I32 || + MI.getOpcode() == Mips::ATOMIC_CMP_SWAP_I64) && + "Unsupported atomic psseudo for EmitAtomicCmpSwap."); + + const unsigned Size = MI.getOpcode() == Mips::ATOMIC_CMP_SWAP_I32 ? 4 : 8; MachineFunction *MF = BB->getParent(); - MachineRegisterInfo &RegInfo = MF->getRegInfo(); + MachineRegisterInfo &MRI = MF->getRegInfo(); const TargetRegisterClass *RC = getRegClassFor(MVT::getIntegerVT(Size * 8)); const TargetInstrInfo *TII = Subtarget.getInstrInfo(); - const bool ArePtrs64bit = ABI.ArePtrs64bit(); DebugLoc DL = MI.getDebugLoc(); - unsigned LL, SC, ZERO, BNE, BEQ; - - if (Size == 4) { - if (isMicroMips) { - LL = Subtarget.hasMips32r6() ? Mips::LL_MMR6 : Mips::LL_MM; - SC = Subtarget.hasMips32r6() ? Mips::SC_MMR6 : Mips::SC_MM; - } else { - LL = Subtarget.hasMips32r6() - ? (ArePtrs64bit ? Mips::LL64_R6 : Mips::LL_R6) - : (ArePtrs64bit ? Mips::LL64 : Mips::LL); - SC = Subtarget.hasMips32r6() - ? (ArePtrs64bit ? Mips::SC64_R6 : Mips::SC_R6) - : (ArePtrs64bit ? Mips::SC64 : Mips::SC); - } - - ZERO = Mips::ZERO; - BNE = Mips::BNE; - BEQ = Mips::BEQ; - } else { - LL = Subtarget.hasMips64r6() ? Mips::LLD_R6 : Mips::LLD; - SC = Subtarget.hasMips64r6() ? Mips::SCD_R6 : Mips::SCD; - ZERO = Mips::ZERO_64; - BNE = Mips::BNE64; - BEQ = Mips::BEQ64; - } + unsigned AtomicOp = MI.getOpcode() == Mips::ATOMIC_CMP_SWAP_I32 + ? Mips::ATOMIC_CMP_SWAP_I32_POSTRA + : Mips::ATOMIC_CMP_SWAP_I64_POSTRA; unsigned Dest = MI.getOperand(0).getReg(); unsigned Ptr = MI.getOperand(1).getReg(); unsigned OldVal = MI.getOperand(2).getReg(); unsigned NewVal = MI.getOperand(3).getReg(); - unsigned Success = RegInfo.createVirtualRegister(RC); + unsigned Scratch = MRI.createVirtualRegister(RC); + MachineBasicBlock::iterator II(MI); - // insert new blocks after the current block - const BasicBlock *LLVM_BB = BB->getBasicBlock(); - MachineBasicBlock *loop1MBB = MF->CreateMachineBasicBlock(LLVM_BB); - MachineBasicBlock *loop2MBB = MF->CreateMachineBasicBlock(LLVM_BB); - MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); - MachineFunction::iterator It = ++BB->getIterator(); - MF->insert(It, loop1MBB); - MF->insert(It, loop2MBB); - MF->insert(It, exitMBB); - - // Transfer the remainder of BB and its successor edges to exitMBB. - exitMBB->splice(exitMBB->begin(), BB, - std::next(MachineBasicBlock::iterator(MI)), BB->end()); - exitMBB->transferSuccessorsAndUpdatePHIs(BB); - - // thisMBB: - // ... - // fallthrough --> loop1MBB - BB->addSuccessor(loop1MBB); - loop1MBB->addSuccessor(exitMBB); - loop1MBB->addSuccessor(loop2MBB); - loop2MBB->addSuccessor(loop1MBB); - loop2MBB->addSuccessor(exitMBB); - - // loop1MBB: - // ll dest, 0(ptr) - // bne dest, oldval, exitMBB - BB = loop1MBB; - BuildMI(BB, DL, TII->get(LL), Dest).addReg(Ptr).addImm(0); - BuildMI(BB, DL, TII->get(BNE)) - .addReg(Dest).addReg(OldVal).addMBB(exitMBB); - - // loop2MBB: - // sc success, newval, 0(ptr) - // beq success, $0, loop1MBB - BB = loop2MBB; - BuildMI(BB, DL, TII->get(SC), Success) - .addReg(NewVal).addReg(Ptr).addImm(0); - BuildMI(BB, DL, TII->get(BEQ)) - .addReg(Success).addReg(ZERO).addMBB(loop1MBB); + // We need to create copies of the various registers and kill them at the + // atomic pseudo. If the copies are not made, when the atomic is expanded + // after fast register allocation, the spills will end up outside of the + // blocks that their values are defined in, causing livein errors. + + unsigned DestCopy = MRI.createVirtualRegister(MRI.getRegClass(Dest)); + unsigned PtrCopy = MRI.createVirtualRegister(MRI.getRegClass(Ptr)); + unsigned OldValCopy = MRI.createVirtualRegister(MRI.getRegClass(OldVal)); + unsigned NewValCopy = MRI.createVirtualRegister(MRI.getRegClass(NewVal)); + + BuildMI(*BB, II, DL, TII->get(Mips::COPY), DestCopy).addReg(Dest); + BuildMI(*BB, II, DL, TII->get(Mips::COPY), PtrCopy).addReg(Ptr); + BuildMI(*BB, II, DL, TII->get(Mips::COPY), OldValCopy).addReg(OldVal); + BuildMI(*BB, II, DL, TII->get(Mips::COPY), NewValCopy).addReg(NewVal); + + // The purposes of the flags on the scratch registers is explained in + // emitAtomicBinary. In summary, we need a scratch register which is going to + // be undef, that is unique among registers chosen for the instruction. + + BuildMI(*BB, II, DL, TII->get(AtomicOp)) + .addReg(Dest, RegState::Define | RegState::EarlyClobber) + .addReg(PtrCopy, RegState::Kill) + .addReg(OldValCopy, RegState::Kill) + .addReg(NewValCopy, RegState::Kill) + .addReg(Scratch, RegState::EarlyClobber | RegState::Define | + RegState::Dead | RegState::Implicit); MI.eraseFromParent(); // The instruction is gone now. - return exitMBB; + return BB; } MachineBasicBlock *MipsTargetLowering::emitAtomicCmpSwapPartword( MachineInstr &MI, MachineBasicBlock *BB, unsigned Size) const { assert((Size == 1 || Size == 2) && "Unsupported size for EmitAtomicCmpSwapPartial."); MachineFunction *MF = BB->getParent(); MachineRegisterInfo &RegInfo = MF->getRegInfo(); const TargetRegisterClass *RC = getRegClassFor(MVT::i32); const bool ArePtrs64bit = ABI.ArePtrs64bit(); const TargetRegisterClass *RCp = getRegClassFor(ArePtrs64bit ? MVT::i64 : MVT::i32); const TargetInstrInfo *TII = Subtarget.getInstrInfo(); DebugLoc DL = MI.getDebugLoc(); unsigned Dest = MI.getOperand(0).getReg(); unsigned Ptr = MI.getOperand(1).getReg(); unsigned CmpVal = MI.getOperand(2).getReg(); unsigned NewVal = MI.getOperand(3).getReg(); unsigned AlignedAddr = RegInfo.createVirtualRegister(RCp); unsigned ShiftAmt = RegInfo.createVirtualRegister(RC); unsigned Mask = RegInfo.createVirtualRegister(RC); unsigned Mask2 = RegInfo.createVirtualRegister(RC); unsigned ShiftedCmpVal = RegInfo.createVirtualRegister(RC); - unsigned OldVal = RegInfo.createVirtualRegister(RC); - unsigned MaskedOldVal0 = RegInfo.createVirtualRegister(RC); unsigned ShiftedNewVal = RegInfo.createVirtualRegister(RC); unsigned MaskLSB2 = RegInfo.createVirtualRegister(RCp); unsigned PtrLSB2 = RegInfo.createVirtualRegister(RC); unsigned MaskUpper = RegInfo.createVirtualRegister(RC); unsigned MaskedCmpVal = RegInfo.createVirtualRegister(RC); unsigned MaskedNewVal = RegInfo.createVirtualRegister(RC); - unsigned MaskedOldVal1 = RegInfo.createVirtualRegister(RC); - unsigned StoreVal = RegInfo.createVirtualRegister(RC); - unsigned SrlRes = RegInfo.createVirtualRegister(RC); - unsigned Success = RegInfo.createVirtualRegister(RC); - unsigned LL, SC; - - if (isMicroMips) { - LL = Subtarget.hasMips32r6() ? Mips::LL_MMR6 : Mips::LL_MM; - SC = Subtarget.hasMips32r6() ? Mips::SC_MMR6 : Mips::SC_MM; - } else { - LL = Subtarget.hasMips32r6() ? (ArePtrs64bit ? Mips::LL64_R6 : Mips::LL_R6) - : (ArePtrs64bit ? Mips::LL64 : Mips::LL); - SC = Subtarget.hasMips32r6() ? (ArePtrs64bit ? Mips::SC64_R6 : Mips::SC_R6) - : (ArePtrs64bit ? Mips::SC64 : Mips::SC); - } + unsigned AtomicOp = MI.getOpcode() == Mips::ATOMIC_CMP_SWAP_I8 + ? Mips::ATOMIC_CMP_SWAP_I8_POSTRA + : Mips::ATOMIC_CMP_SWAP_I16_POSTRA; + + // The scratch registers here with the EarlyClobber | Define | Dead | Implicit + // flags are used to coerce the register allocator and the machine verifier to + // accept the usage of these registers. + // The EarlyClobber flag has the semantic properties that the operand it is + // attached to is clobbered before the rest of the inputs are read. Hence it + // must be unique among the operands to the instruction. + // The Define flag is needed to coerce the machine verifier that an Undef + // value isn't a problem. + // The Dead flag is needed as the value in scratch isn't used by any other + // instruction. Kill isn't used as Dead is more precise. + unsigned Scratch = RegInfo.createVirtualRegister(RC); + unsigned Scratch2 = RegInfo.createVirtualRegister(RC); // insert new blocks after the current block const BasicBlock *LLVM_BB = BB->getBasicBlock(); - MachineBasicBlock *loop1MBB = MF->CreateMachineBasicBlock(LLVM_BB); - MachineBasicBlock *loop2MBB = MF->CreateMachineBasicBlock(LLVM_BB); - MachineBasicBlock *sinkMBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineBasicBlock *exitMBB = MF->CreateMachineBasicBlock(LLVM_BB); MachineFunction::iterator It = ++BB->getIterator(); - MF->insert(It, loop1MBB); - MF->insert(It, loop2MBB); - MF->insert(It, sinkMBB); MF->insert(It, exitMBB); // Transfer the remainder of BB and its successor edges to exitMBB. exitMBB->splice(exitMBB->begin(), BB, std::next(MachineBasicBlock::iterator(MI)), BB->end()); exitMBB->transferSuccessorsAndUpdatePHIs(BB); - BB->addSuccessor(loop1MBB); - loop1MBB->addSuccessor(sinkMBB); - loop1MBB->addSuccessor(loop2MBB); - loop2MBB->addSuccessor(loop1MBB); - loop2MBB->addSuccessor(sinkMBB); - sinkMBB->addSuccessor(exitMBB); + BB->addSuccessor(exitMBB, BranchProbability::getOne()); - // FIXME: computation of newval2 can be moved to loop2MBB. // thisMBB: // addiu masklsb2,$0,-4 # 0xfffffffc // and alignedaddr,ptr,masklsb2 // andi ptrlsb2,ptr,3 // xori ptrlsb2,ptrlsb2,3 # Only for BE // sll shiftamt,ptrlsb2,3 // ori maskupper,$0,255 # 0xff // sll mask,maskupper,shiftamt // nor mask2,$0,mask // andi maskedcmpval,cmpval,255 // sll shiftedcmpval,maskedcmpval,shiftamt // andi maskednewval,newval,255 // sll shiftednewval,maskednewval,shiftamt int64_t MaskImm = (Size == 1) ? 255 : 65535; BuildMI(BB, DL, TII->get(ArePtrs64bit ? Mips::DADDiu : Mips::ADDiu), MaskLSB2) .addReg(ABI.GetNullPtr()).addImm(-4); BuildMI(BB, DL, TII->get(ArePtrs64bit ? Mips::AND64 : Mips::AND), AlignedAddr) .addReg(Ptr).addReg(MaskLSB2); BuildMI(BB, DL, TII->get(Mips::ANDi), PtrLSB2) .addReg(Ptr, 0, ArePtrs64bit ? Mips::sub_32 : 0).addImm(3); if (Subtarget.isLittle()) { BuildMI(BB, DL, TII->get(Mips::SLL), ShiftAmt).addReg(PtrLSB2).addImm(3); } else { unsigned Off = RegInfo.createVirtualRegister(RC); BuildMI(BB, DL, TII->get(Mips::XORi), Off) .addReg(PtrLSB2).addImm((Size == 1) ? 3 : 2); BuildMI(BB, DL, TII->get(Mips::SLL), ShiftAmt).addReg(Off).addImm(3); } BuildMI(BB, DL, TII->get(Mips::ORi), MaskUpper) .addReg(Mips::ZERO).addImm(MaskImm); BuildMI(BB, DL, TII->get(Mips::SLLV), Mask) .addReg(MaskUpper).addReg(ShiftAmt); BuildMI(BB, DL, TII->get(Mips::NOR), Mask2).addReg(Mips::ZERO).addReg(Mask); BuildMI(BB, DL, TII->get(Mips::ANDi), MaskedCmpVal) .addReg(CmpVal).addImm(MaskImm); BuildMI(BB, DL, TII->get(Mips::SLLV), ShiftedCmpVal) .addReg(MaskedCmpVal).addReg(ShiftAmt); BuildMI(BB, DL, TII->get(Mips::ANDi), MaskedNewVal) .addReg(NewVal).addImm(MaskImm); BuildMI(BB, DL, TII->get(Mips::SLLV), ShiftedNewVal) .addReg(MaskedNewVal).addReg(ShiftAmt); - // loop1MBB: - // ll oldval,0(alginedaddr) - // and maskedoldval0,oldval,mask - // bne maskedoldval0,shiftedcmpval,sinkMBB - BB = loop1MBB; - BuildMI(BB, DL, TII->get(LL), OldVal).addReg(AlignedAddr).addImm(0); - BuildMI(BB, DL, TII->get(Mips::AND), MaskedOldVal0) - .addReg(OldVal).addReg(Mask); - BuildMI(BB, DL, TII->get(Mips::BNE)) - .addReg(MaskedOldVal0).addReg(ShiftedCmpVal).addMBB(sinkMBB); - - // loop2MBB: - // and maskedoldval1,oldval,mask2 - // or storeval,maskedoldval1,shiftednewval - // sc success,storeval,0(alignedaddr) - // beq success,$0,loop1MBB - BB = loop2MBB; - BuildMI(BB, DL, TII->get(Mips::AND), MaskedOldVal1) - .addReg(OldVal).addReg(Mask2); - BuildMI(BB, DL, TII->get(Mips::OR), StoreVal) - .addReg(MaskedOldVal1).addReg(ShiftedNewVal); - BuildMI(BB, DL, TII->get(SC), Success) - .addReg(StoreVal).addReg(AlignedAddr).addImm(0); - BuildMI(BB, DL, TII->get(Mips::BEQ)) - .addReg(Success).addReg(Mips::ZERO).addMBB(loop1MBB); - - // sinkMBB: - // srl srlres,maskedoldval0,shiftamt - // sign_extend dest,srlres - BB = sinkMBB; - - BuildMI(BB, DL, TII->get(Mips::SRLV), SrlRes) - .addReg(MaskedOldVal0).addReg(ShiftAmt); - BB = emitSignExtendToI32InReg(MI, BB, Size, Dest, SrlRes); + // The purposes of the flags on the scratch registers are explained in + // emitAtomicBinary. In summary, we need a scratch register which is going to + // be undef, that is unique among the register chosen for the instruction. + + BuildMI(BB, DL, TII->get(AtomicOp)) + .addReg(Dest, RegState::Define | RegState::EarlyClobber) + .addReg(AlignedAddr) + .addReg(Mask) + .addReg(ShiftedCmpVal) + .addReg(Mask2) + .addReg(ShiftedNewVal) + .addReg(ShiftAmt) + .addReg(Scratch, RegState::EarlyClobber | RegState::Define | + RegState::Dead | RegState::Implicit) + .addReg(Scratch2, RegState::EarlyClobber | RegState::Define | + RegState::Dead | RegState::Implicit); MI.eraseFromParent(); // The instruction is gone now. return exitMBB; } SDValue MipsTargetLowering::lowerBRCOND(SDValue Op, SelectionDAG &DAG) const { // The first operand is the chain, the second is the condition, the third is // the block to branch to if the condition is true. SDValue Chain = Op.getOperand(0); SDValue Dest = Op.getOperand(2); SDLoc DL(Op); assert(!Subtarget.hasMips32r6() && !Subtarget.hasMips64r6()); SDValue CondRes = createFPCmp(DAG, Op.getOperand(1)); // Return if flag is not set by a floating point comparison. if (CondRes.getOpcode() != MipsISD::FPCmp) return Op; SDValue CCNode = CondRes.getOperand(2); Mips::CondCode CC = (Mips::CondCode)cast(CCNode)->getZExtValue(); unsigned Opc = invertFPCondCodeUser(CC) ? Mips::BRANCH_F : Mips::BRANCH_T; SDValue BrCode = DAG.getConstant(Opc, DL, MVT::i32); SDValue FCC0 = DAG.getRegister(Mips::FCC0, MVT::i32); return DAG.getNode(MipsISD::FPBrcond, DL, Op.getValueType(), Chain, BrCode, FCC0, Dest, CondRes); } SDValue MipsTargetLowering:: lowerSELECT(SDValue Op, SelectionDAG &DAG) const { assert(!Subtarget.hasMips32r6() && !Subtarget.hasMips64r6()); SDValue Cond = createFPCmp(DAG, Op.getOperand(0)); // Return if flag is not set by a floating point comparison. if (Cond.getOpcode() != MipsISD::FPCmp) return Op; return createCMovFP(DAG, Cond, Op.getOperand(1), Op.getOperand(2), SDLoc(Op)); } SDValue MipsTargetLowering::lowerSETCC(SDValue Op, SelectionDAG &DAG) const { assert(!Subtarget.hasMips32r6() && !Subtarget.hasMips64r6()); SDValue Cond = createFPCmp(DAG, Op); assert(Cond.getOpcode() == MipsISD::FPCmp && "Floating point operand expected."); SDLoc DL(Op); SDValue True = DAG.getConstant(1, DL, MVT::i32); SDValue False = DAG.getConstant(0, DL, MVT::i32); return createCMovFP(DAG, Cond, True, False, DL); } SDValue MipsTargetLowering::lowerGlobalAddress(SDValue Op, SelectionDAG &DAG) const { EVT Ty = Op.getValueType(); GlobalAddressSDNode *N = cast(Op); const GlobalValue *GV = N->getGlobal(); if (!isPositionIndependent()) { const MipsTargetObjectFile *TLOF = static_cast( getTargetMachine().getObjFileLowering()); const GlobalObject *GO = GV->getBaseObject(); if (GO && TLOF->IsGlobalInSmallSection(GO, getTargetMachine())) // %gp_rel relocation return getAddrGPRel(N, SDLoc(N), Ty, DAG, ABI.IsN64()); // %hi/%lo relocation return Subtarget.hasSym32() ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) // %highest/%higher/%hi/%lo relocation : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); } // Every other architecture would use shouldAssumeDSOLocal in here, but // mips is special. // * In PIC code mips requires got loads even for local statics! // * To save on got entries, for local statics the got entry contains the // page and an additional add instruction takes care of the low bits. // * It is legal to access a hidden symbol with a non hidden undefined, // so one cannot guarantee that all access to a hidden symbol will know // it is hidden. // * Mips linkers don't support creating a page and a full got entry for // the same symbol. // * Given all that, we have to use a full got entry for hidden symbols :-( if (GV->hasLocalLinkage()) return getAddrLocal(N, SDLoc(N), Ty, DAG, ABI.IsN32() || ABI.IsN64()); if (LargeGOT) return getAddrGlobalLargeGOT( N, SDLoc(N), Ty, DAG, MipsII::MO_GOT_HI16, MipsII::MO_GOT_LO16, DAG.getEntryNode(), MachinePointerInfo::getGOT(DAG.getMachineFunction())); return getAddrGlobal( N, SDLoc(N), Ty, DAG, (ABI.IsN32() || ABI.IsN64()) ? MipsII::MO_GOT_DISP : MipsII::MO_GOT, DAG.getEntryNode(), MachinePointerInfo::getGOT(DAG.getMachineFunction())); } SDValue MipsTargetLowering::lowerBlockAddress(SDValue Op, SelectionDAG &DAG) const { BlockAddressSDNode *N = cast(Op); EVT Ty = Op.getValueType(); if (!isPositionIndependent()) return Subtarget.hasSym32() ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); return getAddrLocal(N, SDLoc(N), Ty, DAG, ABI.IsN32() || ABI.IsN64()); } SDValue MipsTargetLowering:: lowerGlobalTLSAddress(SDValue Op, SelectionDAG &DAG) const { // If the relocation model is PIC, use the General Dynamic TLS Model or // Local Dynamic TLS model, otherwise use the Initial Exec or // Local Exec TLS Model. GlobalAddressSDNode *GA = cast(Op); if (DAG.getTarget().useEmulatedTLS()) return LowerToTLSEmulatedModel(GA, DAG); SDLoc DL(GA); const GlobalValue *GV = GA->getGlobal(); EVT PtrVT = getPointerTy(DAG.getDataLayout()); TLSModel::Model model = getTargetMachine().getTLSModel(GV); if (model == TLSModel::GeneralDynamic || model == TLSModel::LocalDynamic) { // General Dynamic and Local Dynamic TLS Model. unsigned Flag = (model == TLSModel::LocalDynamic) ? MipsII::MO_TLSLDM : MipsII::MO_TLSGD; SDValue TGA = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, Flag); SDValue Argument = DAG.getNode(MipsISD::Wrapper, DL, PtrVT, getGlobalReg(DAG, PtrVT), TGA); unsigned PtrSize = PtrVT.getSizeInBits(); IntegerType *PtrTy = Type::getIntNTy(*DAG.getContext(), PtrSize); SDValue TlsGetAddr = DAG.getExternalSymbol("__tls_get_addr", PtrVT); ArgListTy Args; ArgListEntry Entry; Entry.Node = Argument; Entry.Ty = PtrTy; Args.push_back(Entry); TargetLowering::CallLoweringInfo CLI(DAG); CLI.setDebugLoc(DL) .setChain(DAG.getEntryNode()) .setLibCallee(CallingConv::C, PtrTy, TlsGetAddr, std::move(Args)); std::pair CallResult = LowerCallTo(CLI); SDValue Ret = CallResult.first; if (model != TLSModel::LocalDynamic) return Ret; SDValue TGAHi = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, MipsII::MO_DTPREL_HI); SDValue Hi = DAG.getNode(MipsISD::Hi, DL, PtrVT, TGAHi); SDValue TGALo = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, MipsII::MO_DTPREL_LO); SDValue Lo = DAG.getNode(MipsISD::Lo, DL, PtrVT, TGALo); SDValue Add = DAG.getNode(ISD::ADD, DL, PtrVT, Hi, Ret); return DAG.getNode(ISD::ADD, DL, PtrVT, Add, Lo); } SDValue Offset; if (model == TLSModel::InitialExec) { // Initial Exec TLS Model SDValue TGA = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, MipsII::MO_GOTTPREL); TGA = DAG.getNode(MipsISD::Wrapper, DL, PtrVT, getGlobalReg(DAG, PtrVT), TGA); Offset = DAG.getLoad(PtrVT, DL, DAG.getEntryNode(), TGA, MachinePointerInfo()); } else { // Local Exec TLS Model assert(model == TLSModel::LocalExec); SDValue TGAHi = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, MipsII::MO_TPREL_HI); SDValue TGALo = DAG.getTargetGlobalAddress(GV, DL, PtrVT, 0, MipsII::MO_TPREL_LO); SDValue Hi = DAG.getNode(MipsISD::Hi, DL, PtrVT, TGAHi); SDValue Lo = DAG.getNode(MipsISD::Lo, DL, PtrVT, TGALo); Offset = DAG.getNode(ISD::ADD, DL, PtrVT, Hi, Lo); } SDValue ThreadPointer = DAG.getNode(MipsISD::ThreadPointer, DL, PtrVT); return DAG.getNode(ISD::ADD, DL, PtrVT, ThreadPointer, Offset); } SDValue MipsTargetLowering:: lowerJumpTable(SDValue Op, SelectionDAG &DAG) const { JumpTableSDNode *N = cast(Op); EVT Ty = Op.getValueType(); if (!isPositionIndependent()) return Subtarget.hasSym32() ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); return getAddrLocal(N, SDLoc(N), Ty, DAG, ABI.IsN32() || ABI.IsN64()); } SDValue MipsTargetLowering:: lowerConstantPool(SDValue Op, SelectionDAG &DAG) const { ConstantPoolSDNode *N = cast(Op); EVT Ty = Op.getValueType(); if (!isPositionIndependent()) { const MipsTargetObjectFile *TLOF = static_cast( getTargetMachine().getObjFileLowering()); if (TLOF->IsConstantInSmallSection(DAG.getDataLayout(), N->getConstVal(), getTargetMachine())) // %gp_rel relocation return getAddrGPRel(N, SDLoc(N), Ty, DAG, ABI.IsN64()); return Subtarget.hasSym32() ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); } return getAddrLocal(N, SDLoc(N), Ty, DAG, ABI.IsN32() || ABI.IsN64()); } SDValue MipsTargetLowering::lowerVASTART(SDValue Op, SelectionDAG &DAG) const { MachineFunction &MF = DAG.getMachineFunction(); MipsFunctionInfo *FuncInfo = MF.getInfo(); SDLoc DL(Op); SDValue FI = DAG.getFrameIndex(FuncInfo->getVarArgsFrameIndex(), getPointerTy(MF.getDataLayout())); // vastart just stores the address of the VarArgsFrameIndex slot into the // memory location argument. const Value *SV = cast(Op.getOperand(2))->getValue(); return DAG.getStore(Op.getOperand(0), DL, FI, Op.getOperand(1), MachinePointerInfo(SV)); } SDValue MipsTargetLowering::lowerVAARG(SDValue Op, SelectionDAG &DAG) const { SDNode *Node = Op.getNode(); EVT VT = Node->getValueType(0); SDValue Chain = Node->getOperand(0); SDValue VAListPtr = Node->getOperand(1); unsigned Align = Node->getConstantOperandVal(3); const Value *SV = cast(Node->getOperand(2))->getValue(); SDLoc DL(Node); unsigned ArgSlotSizeInBytes = (ABI.IsN32() || ABI.IsN64()) ? 8 : 4; SDValue VAListLoad = DAG.getLoad(getPointerTy(DAG.getDataLayout()), DL, Chain, VAListPtr, MachinePointerInfo(SV)); SDValue VAList = VAListLoad; // Re-align the pointer if necessary. // It should only ever be necessary for 64-bit types on O32 since the minimum // argument alignment is the same as the maximum type alignment for N32/N64. // // FIXME: We currently align too often. The code generator doesn't notice // when the pointer is still aligned from the last va_arg (or pair of // va_args for the i64 on O32 case). if (Align > getMinStackArgumentAlignment()) { assert(((Align & (Align-1)) == 0) && "Expected Align to be a power of 2"); VAList = DAG.getNode(ISD::ADD, DL, VAList.getValueType(), VAList, DAG.getConstant(Align - 1, DL, VAList.getValueType())); VAList = DAG.getNode(ISD::AND, DL, VAList.getValueType(), VAList, DAG.getConstant(-(int64_t)Align, DL, VAList.getValueType())); } // Increment the pointer, VAList, to the next vaarg. auto &TD = DAG.getDataLayout(); unsigned ArgSizeInBytes = TD.getTypeAllocSize(VT.getTypeForEVT(*DAG.getContext())); SDValue Tmp3 = DAG.getNode(ISD::ADD, DL, VAList.getValueType(), VAList, DAG.getConstant(alignTo(ArgSizeInBytes, ArgSlotSizeInBytes), DL, VAList.getValueType())); // Store the incremented VAList to the legalized pointer Chain = DAG.getStore(VAListLoad.getValue(1), DL, Tmp3, VAListPtr, MachinePointerInfo(SV)); // In big-endian mode we must adjust the pointer when the load size is smaller // than the argument slot size. We must also reduce the known alignment to // match. For example in the N64 ABI, we must add 4 bytes to the offset to get // the correct half of the slot, and reduce the alignment from 8 (slot // alignment) down to 4 (type alignment). if (!Subtarget.isLittle() && ArgSizeInBytes < ArgSlotSizeInBytes) { unsigned Adjustment = ArgSlotSizeInBytes - ArgSizeInBytes; VAList = DAG.getNode(ISD::ADD, DL, VAListPtr.getValueType(), VAList, DAG.getIntPtrConstant(Adjustment, DL)); } // Load the actual argument out of the pointer VAList return DAG.getLoad(VT, DL, Chain, VAList, MachinePointerInfo()); } static SDValue lowerFCOPYSIGN32(SDValue Op, SelectionDAG &DAG, bool HasExtractInsert) { EVT TyX = Op.getOperand(0).getValueType(); EVT TyY = Op.getOperand(1).getValueType(); SDLoc DL(Op); SDValue Const1 = DAG.getConstant(1, DL, MVT::i32); SDValue Const31 = DAG.getConstant(31, DL, MVT::i32); SDValue Res; // If operand is of type f64, extract the upper 32-bit. Otherwise, bitcast it // to i32. SDValue X = (TyX == MVT::f32) ? DAG.getNode(ISD::BITCAST, DL, MVT::i32, Op.getOperand(0)) : DAG.getNode(MipsISD::ExtractElementF64, DL, MVT::i32, Op.getOperand(0), Const1); SDValue Y = (TyY == MVT::f32) ? DAG.getNode(ISD::BITCAST, DL, MVT::i32, Op.getOperand(1)) : DAG.getNode(MipsISD::ExtractElementF64, DL, MVT::i32, Op.getOperand(1), Const1); if (HasExtractInsert) { // ext E, Y, 31, 1 ; extract bit31 of Y // ins X, E, 31, 1 ; insert extracted bit at bit31 of X SDValue E = DAG.getNode(MipsISD::Ext, DL, MVT::i32, Y, Const31, Const1); Res = DAG.getNode(MipsISD::Ins, DL, MVT::i32, E, Const31, Const1, X); } else { // sll SllX, X, 1 // srl SrlX, SllX, 1 // srl SrlY, Y, 31 // sll SllY, SrlX, 31 // or Or, SrlX, SllY SDValue SllX = DAG.getNode(ISD::SHL, DL, MVT::i32, X, Const1); SDValue SrlX = DAG.getNode(ISD::SRL, DL, MVT::i32, SllX, Const1); SDValue SrlY = DAG.getNode(ISD::SRL, DL, MVT::i32, Y, Const31); SDValue SllY = DAG.getNode(ISD::SHL, DL, MVT::i32, SrlY, Const31); Res = DAG.getNode(ISD::OR, DL, MVT::i32, SrlX, SllY); } if (TyX == MVT::f32) return DAG.getNode(ISD::BITCAST, DL, Op.getOperand(0).getValueType(), Res); SDValue LowX = DAG.getNode(MipsISD::ExtractElementF64, DL, MVT::i32, Op.getOperand(0), DAG.getConstant(0, DL, MVT::i32)); return DAG.getNode(MipsISD::BuildPairF64, DL, MVT::f64, LowX, Res); } static SDValue lowerFCOPYSIGN64(SDValue Op, SelectionDAG &DAG, bool HasExtractInsert) { unsigned WidthX = Op.getOperand(0).getValueSizeInBits(); unsigned WidthY = Op.getOperand(1).getValueSizeInBits(); EVT TyX = MVT::getIntegerVT(WidthX), TyY = MVT::getIntegerVT(WidthY); SDLoc DL(Op); SDValue Const1 = DAG.getConstant(1, DL, MVT::i32); // Bitcast to integer nodes. SDValue X = DAG.getNode(ISD::BITCAST, DL, TyX, Op.getOperand(0)); SDValue Y = DAG.getNode(ISD::BITCAST, DL, TyY, Op.getOperand(1)); if (HasExtractInsert) { // ext E, Y, width(Y) - 1, 1 ; extract bit width(Y)-1 of Y // ins X, E, width(X) - 1, 1 ; insert extracted bit at bit width(X)-1 of X SDValue E = DAG.getNode(MipsISD::Ext, DL, TyY, Y, DAG.getConstant(WidthY - 1, DL, MVT::i32), Const1); if (WidthX > WidthY) E = DAG.getNode(ISD::ZERO_EXTEND, DL, TyX, E); else if (WidthY > WidthX) E = DAG.getNode(ISD::TRUNCATE, DL, TyX, E); SDValue I = DAG.getNode(MipsISD::Ins, DL, TyX, E, DAG.getConstant(WidthX - 1, DL, MVT::i32), Const1, X); return DAG.getNode(ISD::BITCAST, DL, Op.getOperand(0).getValueType(), I); } // (d)sll SllX, X, 1 // (d)srl SrlX, SllX, 1 // (d)srl SrlY, Y, width(Y)-1 // (d)sll SllY, SrlX, width(Y)-1 // or Or, SrlX, SllY SDValue SllX = DAG.getNode(ISD::SHL, DL, TyX, X, Const1); SDValue SrlX = DAG.getNode(ISD::SRL, DL, TyX, SllX, Const1); SDValue SrlY = DAG.getNode(ISD::SRL, DL, TyY, Y, DAG.getConstant(WidthY - 1, DL, MVT::i32)); if (WidthX > WidthY) SrlY = DAG.getNode(ISD::ZERO_EXTEND, DL, TyX, SrlY); else if (WidthY > WidthX) SrlY = DAG.getNode(ISD::TRUNCATE, DL, TyX, SrlY); SDValue SllY = DAG.getNode(ISD::SHL, DL, TyX, SrlY, DAG.getConstant(WidthX - 1, DL, MVT::i32)); SDValue Or = DAG.getNode(ISD::OR, DL, TyX, SrlX, SllY); return DAG.getNode(ISD::BITCAST, DL, Op.getOperand(0).getValueType(), Or); } SDValue MipsTargetLowering::lowerFCOPYSIGN(SDValue Op, SelectionDAG &DAG) const { if (Subtarget.isGP64bit()) return lowerFCOPYSIGN64(Op, DAG, Subtarget.hasExtractInsert()); return lowerFCOPYSIGN32(Op, DAG, Subtarget.hasExtractInsert()); } SDValue MipsTargetLowering:: lowerFRAMEADDR(SDValue Op, SelectionDAG &DAG) const { // check the depth assert((cast(Op.getOperand(0))->getZExtValue() == 0) && "Frame address can only be determined for current frame."); MachineFrameInfo &MFI = DAG.getMachineFunction().getFrameInfo(); MFI.setFrameAddressIsTaken(true); EVT VT = Op.getValueType(); SDLoc DL(Op); SDValue FrameAddr = DAG.getCopyFromReg( DAG.getEntryNode(), DL, ABI.IsN64() ? Mips::FP_64 : Mips::FP, VT); return FrameAddr; } SDValue MipsTargetLowering::lowerRETURNADDR(SDValue Op, SelectionDAG &DAG) const { if (verifyReturnAddressArgumentIsConstant(Op, DAG)) return SDValue(); // check the depth assert((cast(Op.getOperand(0))->getZExtValue() == 0) && "Return address can be determined only for current frame."); MachineFunction &MF = DAG.getMachineFunction(); MachineFrameInfo &MFI = MF.getFrameInfo(); MVT VT = Op.getSimpleValueType(); unsigned RA = ABI.IsN64() ? Mips::RA_64 : Mips::RA; MFI.setReturnAddressIsTaken(true); // Return RA, which contains the return address. Mark it an implicit live-in. unsigned Reg = MF.addLiveIn(RA, getRegClassFor(VT)); return DAG.getCopyFromReg(DAG.getEntryNode(), SDLoc(Op), Reg, VT); } // An EH_RETURN is the result of lowering llvm.eh.return which in turn is // generated from __builtin_eh_return (offset, handler) // The effect of this is to adjust the stack pointer by "offset" // and then branch to "handler". SDValue MipsTargetLowering::lowerEH_RETURN(SDValue Op, SelectionDAG &DAG) const { MachineFunction &MF = DAG.getMachineFunction(); MipsFunctionInfo *MipsFI = MF.getInfo(); MipsFI->setCallsEhReturn(); SDValue Chain = Op.getOperand(0); SDValue Offset = Op.getOperand(1); SDValue Handler = Op.getOperand(2); SDLoc DL(Op); EVT Ty = ABI.IsN64() ? MVT::i64 : MVT::i32; // Store stack offset in V1, store jump target in V0. Glue CopyToReg and // EH_RETURN nodes, so that instructions are emitted back-to-back. unsigned OffsetReg = ABI.IsN64() ? Mips::V1_64 : Mips::V1; unsigned AddrReg = ABI.IsN64() ? Mips::V0_64 : Mips::V0; Chain = DAG.getCopyToReg(Chain, DL, OffsetReg, Offset, SDValue()); Chain = DAG.getCopyToReg(Chain, DL, AddrReg, Handler, Chain.getValue(1)); return DAG.getNode(MipsISD::EH_RETURN, DL, MVT::Other, Chain, DAG.getRegister(OffsetReg, Ty), DAG.getRegister(AddrReg, getPointerTy(MF.getDataLayout())), Chain.getValue(1)); } SDValue MipsTargetLowering::lowerATOMIC_FENCE(SDValue Op, SelectionDAG &DAG) const { // FIXME: Need pseudo-fence for 'singlethread' fences // FIXME: Set SType for weaker fences where supported/appropriate. unsigned SType = 0; SDLoc DL(Op); return DAG.getNode(MipsISD::Sync, DL, MVT::Other, Op.getOperand(0), DAG.getConstant(SType, DL, MVT::i32)); } SDValue MipsTargetLowering::lowerShiftLeftParts(SDValue Op, SelectionDAG &DAG) const { SDLoc DL(Op); MVT VT = Subtarget.isGP64bit() ? MVT::i64 : MVT::i32; SDValue Lo = Op.getOperand(0), Hi = Op.getOperand(1); SDValue Shamt = Op.getOperand(2); // if shamt < (VT.bits): // lo = (shl lo, shamt) // hi = (or (shl hi, shamt) (srl (srl lo, 1), ~shamt)) // else: // lo = 0 // hi = (shl lo, shamt[4:0]) SDValue Not = DAG.getNode(ISD::XOR, DL, MVT::i32, Shamt, DAG.getConstant(-1, DL, MVT::i32)); SDValue ShiftRight1Lo = DAG.getNode(ISD::SRL, DL, VT, Lo, DAG.getConstant(1, DL, VT)); SDValue ShiftRightLo = DAG.getNode(ISD::SRL, DL, VT, ShiftRight1Lo, Not); SDValue ShiftLeftHi = DAG.getNode(ISD::SHL, DL, VT, Hi, Shamt); SDValue Or = DAG.getNode(ISD::OR, DL, VT, ShiftLeftHi, ShiftRightLo); SDValue ShiftLeftLo = DAG.getNode(ISD::SHL, DL, VT, Lo, Shamt); SDValue Cond = DAG.getNode(ISD::AND, DL, MVT::i32, Shamt, DAG.getConstant(VT.getSizeInBits(), DL, MVT::i32)); Lo = DAG.getNode(ISD::SELECT, DL, VT, Cond, DAG.getConstant(0, DL, VT), ShiftLeftLo); Hi = DAG.getNode(ISD::SELECT, DL, VT, Cond, ShiftLeftLo, Or); SDValue Ops[2] = {Lo, Hi}; return DAG.getMergeValues(Ops, DL); } SDValue MipsTargetLowering::lowerShiftRightParts(SDValue Op, SelectionDAG &DAG, bool IsSRA) const { SDLoc DL(Op); SDValue Lo = Op.getOperand(0), Hi = Op.getOperand(1); SDValue Shamt = Op.getOperand(2); MVT VT = Subtarget.isGP64bit() ? MVT::i64 : MVT::i32; // if shamt < (VT.bits): // lo = (or (shl (shl hi, 1), ~shamt) (srl lo, shamt)) // if isSRA: // hi = (sra hi, shamt) // else: // hi = (srl hi, shamt) // else: // if isSRA: // lo = (sra hi, shamt[4:0]) // hi = (sra hi, 31) // else: // lo = (srl hi, shamt[4:0]) // hi = 0 SDValue Not = DAG.getNode(ISD::XOR, DL, MVT::i32, Shamt, DAG.getConstant(-1, DL, MVT::i32)); SDValue ShiftLeft1Hi = DAG.getNode(ISD::SHL, DL, VT, Hi, DAG.getConstant(1, DL, VT)); SDValue ShiftLeftHi = DAG.getNode(ISD::SHL, DL, VT, ShiftLeft1Hi, Not); SDValue ShiftRightLo = DAG.getNode(ISD::SRL, DL, VT, Lo, Shamt); SDValue Or = DAG.getNode(ISD::OR, DL, VT, ShiftLeftHi, ShiftRightLo); SDValue ShiftRightHi = DAG.getNode(IsSRA ? ISD::SRA : ISD::SRL, DL, VT, Hi, Shamt); SDValue Cond = DAG.getNode(ISD::AND, DL, MVT::i32, Shamt, DAG.getConstant(VT.getSizeInBits(), DL, MVT::i32)); SDValue Ext = DAG.getNode(ISD::SRA, DL, VT, Hi, DAG.getConstant(VT.getSizeInBits() - 1, DL, VT)); Lo = DAG.getNode(ISD::SELECT, DL, VT, Cond, ShiftRightHi, Or); Hi = DAG.getNode(ISD::SELECT, DL, VT, Cond, IsSRA ? Ext : DAG.getConstant(0, DL, VT), ShiftRightHi); SDValue Ops[2] = {Lo, Hi}; return DAG.getMergeValues(Ops, DL); } static SDValue createLoadLR(unsigned Opc, SelectionDAG &DAG, LoadSDNode *LD, SDValue Chain, SDValue Src, unsigned Offset) { SDValue Ptr = LD->getBasePtr(); EVT VT = LD->getValueType(0), MemVT = LD->getMemoryVT(); EVT BasePtrVT = Ptr.getValueType(); SDLoc DL(LD); SDVTList VTList = DAG.getVTList(VT, MVT::Other); if (Offset) Ptr = DAG.getNode(ISD::ADD, DL, BasePtrVT, Ptr, DAG.getConstant(Offset, DL, BasePtrVT)); SDValue Ops[] = { Chain, Ptr, Src }; return DAG.getMemIntrinsicNode(Opc, DL, VTList, Ops, MemVT, LD->getMemOperand()); } // Expand an unaligned 32 or 64-bit integer load node. SDValue MipsTargetLowering::lowerLOAD(SDValue Op, SelectionDAG &DAG) const { LoadSDNode *LD = cast(Op); EVT MemVT = LD->getMemoryVT(); if (Subtarget.systemSupportsUnalignedAccess()) return Op; // Return if load is aligned or if MemVT is neither i32 nor i64. if ((LD->getAlignment() >= MemVT.getSizeInBits() / 8) || ((MemVT != MVT::i32) && (MemVT != MVT::i64))) return SDValue(); bool IsLittle = Subtarget.isLittle(); EVT VT = Op.getValueType(); ISD::LoadExtType ExtType = LD->getExtensionType(); SDValue Chain = LD->getChain(), Undef = DAG.getUNDEF(VT); assert((VT == MVT::i32) || (VT == MVT::i64)); // Expand // (set dst, (i64 (load baseptr))) // to // (set tmp, (ldl (add baseptr, 7), undef)) // (set dst, (ldr baseptr, tmp)) if ((VT == MVT::i64) && (ExtType == ISD::NON_EXTLOAD)) { SDValue LDL = createLoadLR(MipsISD::LDL, DAG, LD, Chain, Undef, IsLittle ? 7 : 0); return createLoadLR(MipsISD::LDR, DAG, LD, LDL.getValue(1), LDL, IsLittle ? 0 : 7); } SDValue LWL = createLoadLR(MipsISD::LWL, DAG, LD, Chain, Undef, IsLittle ? 3 : 0); SDValue LWR = createLoadLR(MipsISD::LWR, DAG, LD, LWL.getValue(1), LWL, IsLittle ? 0 : 3); // Expand // (set dst, (i32 (load baseptr))) or // (set dst, (i64 (sextload baseptr))) or // (set dst, (i64 (extload baseptr))) // to // (set tmp, (lwl (add baseptr, 3), undef)) // (set dst, (lwr baseptr, tmp)) if ((VT == MVT::i32) || (ExtType == ISD::SEXTLOAD) || (ExtType == ISD::EXTLOAD)) return LWR; assert((VT == MVT::i64) && (ExtType == ISD::ZEXTLOAD)); // Expand // (set dst, (i64 (zextload baseptr))) // to // (set tmp0, (lwl (add baseptr, 3), undef)) // (set tmp1, (lwr baseptr, tmp0)) // (set tmp2, (shl tmp1, 32)) // (set dst, (srl tmp2, 32)) SDLoc DL(LD); SDValue Const32 = DAG.getConstant(32, DL, MVT::i32); SDValue SLL = DAG.getNode(ISD::SHL, DL, MVT::i64, LWR, Const32); SDValue SRL = DAG.getNode(ISD::SRL, DL, MVT::i64, SLL, Const32); SDValue Ops[] = { SRL, LWR.getValue(1) }; return DAG.getMergeValues(Ops, DL); } static SDValue createStoreLR(unsigned Opc, SelectionDAG &DAG, StoreSDNode *SD, SDValue Chain, unsigned Offset) { SDValue Ptr = SD->getBasePtr(), Value = SD->getValue(); EVT MemVT = SD->getMemoryVT(), BasePtrVT = Ptr.getValueType(); SDLoc DL(SD); SDVTList VTList = DAG.getVTList(MVT::Other); if (Offset) Ptr = DAG.getNode(ISD::ADD, DL, BasePtrVT, Ptr, DAG.getConstant(Offset, DL, BasePtrVT)); SDValue Ops[] = { Chain, Value, Ptr }; return DAG.getMemIntrinsicNode(Opc, DL, VTList, Ops, MemVT, SD->getMemOperand()); } // Expand an unaligned 32 or 64-bit integer store node. static SDValue lowerUnalignedIntStore(StoreSDNode *SD, SelectionDAG &DAG, bool IsLittle) { SDValue Value = SD->getValue(), Chain = SD->getChain(); EVT VT = Value.getValueType(); // Expand // (store val, baseptr) or // (truncstore val, baseptr) // to // (swl val, (add baseptr, 3)) // (swr val, baseptr) if ((VT == MVT::i32) || SD->isTruncatingStore()) { SDValue SWL = createStoreLR(MipsISD::SWL, DAG, SD, Chain, IsLittle ? 3 : 0); return createStoreLR(MipsISD::SWR, DAG, SD, SWL, IsLittle ? 0 : 3); } assert(VT == MVT::i64); // Expand // (store val, baseptr) // to // (sdl val, (add baseptr, 7)) // (sdr val, baseptr) SDValue SDL = createStoreLR(MipsISD::SDL, DAG, SD, Chain, IsLittle ? 7 : 0); return createStoreLR(MipsISD::SDR, DAG, SD, SDL, IsLittle ? 0 : 7); } // Lower (store (fp_to_sint $fp) $ptr) to (store (TruncIntFP $fp), $ptr). static SDValue lowerFP_TO_SINT_STORE(StoreSDNode *SD, SelectionDAG &DAG) { SDValue Val = SD->getValue(); if (Val.getOpcode() != ISD::FP_TO_SINT) return SDValue(); EVT FPTy = EVT::getFloatingPointVT(Val.getValueSizeInBits()); SDValue Tr = DAG.getNode(MipsISD::TruncIntFP, SDLoc(Val), FPTy, Val.getOperand(0)); return DAG.getStore(SD->getChain(), SDLoc(SD), Tr, SD->getBasePtr(), SD->getPointerInfo(), SD->getAlignment(), SD->getMemOperand()->getFlags()); } SDValue MipsTargetLowering::lowerSTORE(SDValue Op, SelectionDAG &DAG) const { StoreSDNode *SD = cast(Op); EVT MemVT = SD->getMemoryVT(); // Lower unaligned integer stores. if (!Subtarget.systemSupportsUnalignedAccess() && (SD->getAlignment() < MemVT.getSizeInBits() / 8) && ((MemVT == MVT::i32) || (MemVT == MVT::i64))) return lowerUnalignedIntStore(SD, DAG, Subtarget.isLittle()); return lowerFP_TO_SINT_STORE(SD, DAG); } SDValue MipsTargetLowering::lowerEH_DWARF_CFA(SDValue Op, SelectionDAG &DAG) const { // Return a fixed StackObject with offset 0 which points to the old stack // pointer. MachineFrameInfo &MFI = DAG.getMachineFunction().getFrameInfo(); EVT ValTy = Op->getValueType(0); int FI = MFI.CreateFixedObject(Op.getValueSizeInBits() / 8, 0, false); return DAG.getFrameIndex(FI, ValTy); } SDValue MipsTargetLowering::lowerFP_TO_SINT(SDValue Op, SelectionDAG &DAG) const { EVT FPTy = EVT::getFloatingPointVT(Op.getValueSizeInBits()); SDValue Trunc = DAG.getNode(MipsISD::TruncIntFP, SDLoc(Op), FPTy, Op.getOperand(0)); return DAG.getNode(ISD::BITCAST, SDLoc(Op), Op.getValueType(), Trunc); } //===----------------------------------------------------------------------===// // Calling Convention Implementation //===----------------------------------------------------------------------===// //===----------------------------------------------------------------------===// // TODO: Implement a generic logic using tblgen that can support this. // Mips O32 ABI rules: // --- // i32 - Passed in A0, A1, A2, A3 and stack // f32 - Only passed in f32 registers if no int reg has been used yet to hold // an argument. Otherwise, passed in A1, A2, A3 and stack. // f64 - Only passed in two aliased f32 registers if no int reg has been used // yet to hold an argument. Otherwise, use A2, A3 and stack. If A1 is // not used, it must be shadowed. If only A3 is available, shadow it and // go to stack. // vXiX - Received as scalarized i32s, passed in A0 - A3 and the stack. // vXf32 - Passed in either a pair of registers {A0, A1}, {A2, A3} or {A0 - A3} // with the remainder spilled to the stack. // vXf64 - Passed in either {A0, A1, A2, A3} or {A2, A3} and in both cases // spilling the remainder to the stack. // // For vararg functions, all arguments are passed in A0, A1, A2, A3 and stack. //===----------------------------------------------------------------------===// static bool CC_MipsO32(unsigned ValNo, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, CCState &State, ArrayRef F64Regs) { const MipsSubtarget &Subtarget = static_cast( State.getMachineFunction().getSubtarget()); static const MCPhysReg IntRegs[] = { Mips::A0, Mips::A1, Mips::A2, Mips::A3 }; const MipsCCState * MipsState = static_cast(&State); static const MCPhysReg F32Regs[] = { Mips::F12, Mips::F14 }; static const MCPhysReg FloatVectorIntRegs[] = { Mips::A0, Mips::A2 }; // Do not process byval args here. if (ArgFlags.isByVal()) return true; // Promote i8 and i16 if (ArgFlags.isInReg() && !Subtarget.isLittle()) { if (LocVT == MVT::i8 || LocVT == MVT::i16 || LocVT == MVT::i32) { LocVT = MVT::i32; if (ArgFlags.isSExt()) LocInfo = CCValAssign::SExtUpper; else if (ArgFlags.isZExt()) LocInfo = CCValAssign::ZExtUpper; else LocInfo = CCValAssign::AExtUpper; } } // Promote i8 and i16 if (LocVT == MVT::i8 || LocVT == MVT::i16) { LocVT = MVT::i32; if (ArgFlags.isSExt()) LocInfo = CCValAssign::SExt; else if (ArgFlags.isZExt()) LocInfo = CCValAssign::ZExt; else LocInfo = CCValAssign::AExt; } unsigned Reg; // f32 and f64 are allocated in A0, A1, A2, A3 when either of the following // is true: function is vararg, argument is 3rd or higher, there is previous // argument which is not f32 or f64. bool AllocateFloatsInIntReg = State.isVarArg() || ValNo > 1 || State.getFirstUnallocated(F32Regs) != ValNo; unsigned OrigAlign = ArgFlags.getOrigAlign(); bool isI64 = (ValVT == MVT::i32 && OrigAlign == 8); bool isVectorFloat = MipsState->WasOriginalArgVectorFloat(ValNo); // The MIPS vector ABI for floats passes them in a pair of registers if (ValVT == MVT::i32 && isVectorFloat) { // This is the start of an vector that was scalarized into an unknown number // of components. It doesn't matter how many there are. Allocate one of the // notional 8 byte aligned registers which map onto the argument stack, and // shadow the register lost to alignment requirements. if (ArgFlags.isSplit()) { Reg = State.AllocateReg(FloatVectorIntRegs); if (Reg == Mips::A2) State.AllocateReg(Mips::A1); else if (Reg == 0) State.AllocateReg(Mips::A3); } else { // If we're an intermediate component of the split, we can just attempt to // allocate a register directly. Reg = State.AllocateReg(IntRegs); } } else if (ValVT == MVT::i32 || (ValVT == MVT::f32 && AllocateFloatsInIntReg)) { Reg = State.AllocateReg(IntRegs); // If this is the first part of an i64 arg, // the allocated register must be either A0 or A2. if (isI64 && (Reg == Mips::A1 || Reg == Mips::A3)) Reg = State.AllocateReg(IntRegs); LocVT = MVT::i32; } else if (ValVT == MVT::f64 && AllocateFloatsInIntReg) { // Allocate int register and shadow next int register. If first // available register is Mips::A1 or Mips::A3, shadow it too. Reg = State.AllocateReg(IntRegs); if (Reg == Mips::A1 || Reg == Mips::A3) Reg = State.AllocateReg(IntRegs); State.AllocateReg(IntRegs); LocVT = MVT::i32; } else if (ValVT.isFloatingPoint() && !AllocateFloatsInIntReg) { // we are guaranteed to find an available float register if (ValVT == MVT::f32) { Reg = State.AllocateReg(F32Regs); // Shadow int register State.AllocateReg(IntRegs); } else { Reg = State.AllocateReg(F64Regs); // Shadow int registers unsigned Reg2 = State.AllocateReg(IntRegs); if (Reg2 == Mips::A1 || Reg2 == Mips::A3) State.AllocateReg(IntRegs); State.AllocateReg(IntRegs); } } else llvm_unreachable("Cannot handle this ValVT."); if (!Reg) { unsigned Offset = State.AllocateStack(ValVT.getStoreSize(), OrigAlign); State.addLoc(CCValAssign::getMem(ValNo, ValVT, Offset, LocVT, LocInfo)); } else State.addLoc(CCValAssign::getReg(ValNo, ValVT, Reg, LocVT, LocInfo)); return false; } static bool CC_MipsO32_FP32(unsigned ValNo, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, CCState &State) { static const MCPhysReg F64Regs[] = { Mips::D6, Mips::D7 }; return CC_MipsO32(ValNo, ValVT, LocVT, LocInfo, ArgFlags, State, F64Regs); } static bool CC_MipsO32_FP64(unsigned ValNo, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, CCState &State) { static const MCPhysReg F64Regs[] = { Mips::D12_64, Mips::D14_64 }; return CC_MipsO32(ValNo, ValVT, LocVT, LocInfo, ArgFlags, State, F64Regs); } static bool CC_MipsO32(unsigned ValNo, MVT ValVT, MVT LocVT, CCValAssign::LocInfo LocInfo, ISD::ArgFlagsTy ArgFlags, CCState &State) LLVM_ATTRIBUTE_UNUSED; #include "MipsGenCallingConv.inc" CCAssignFn *MipsTargetLowering::CCAssignFnForCall() const{ return CC_Mips; } CCAssignFn *MipsTargetLowering::CCAssignFnForReturn() const{ return RetCC_Mips; } //===----------------------------------------------------------------------===// // Call Calling Convention Implementation //===----------------------------------------------------------------------===// // Return next O32 integer argument register. static unsigned getNextIntArgReg(unsigned Reg) { assert((Reg == Mips::A0) || (Reg == Mips::A2)); return (Reg == Mips::A0) ? Mips::A1 : Mips::A3; } SDValue MipsTargetLowering::passArgOnStack(SDValue StackPtr, unsigned Offset, SDValue Chain, SDValue Arg, const SDLoc &DL, bool IsTailCall, SelectionDAG &DAG) const { if (!IsTailCall) { SDValue PtrOff = DAG.getNode(ISD::ADD, DL, getPointerTy(DAG.getDataLayout()), StackPtr, DAG.getIntPtrConstant(Offset, DL)); return DAG.getStore(Chain, DL, Arg, PtrOff, MachinePointerInfo()); } MachineFrameInfo &MFI = DAG.getMachineFunction().getFrameInfo(); int FI = MFI.CreateFixedObject(Arg.getValueSizeInBits() / 8, Offset, false); SDValue FIN = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); return DAG.getStore(Chain, DL, Arg, FIN, MachinePointerInfo(), /* Alignment = */ 0, MachineMemOperand::MOVolatile); } void MipsTargetLowering:: getOpndList(SmallVectorImpl &Ops, std::deque> &RegsToPass, bool IsPICCall, bool GlobalOrExternal, bool InternalLinkage, bool IsCallReloc, CallLoweringInfo &CLI, SDValue Callee, SDValue Chain) const { // Insert node "GP copy globalreg" before call to function. // // R_MIPS_CALL* operators (emitted when non-internal functions are called // in PIC mode) allow symbols to be resolved via lazy binding. // The lazy binding stub requires GP to point to the GOT. // Note that we don't need GP to point to the GOT for indirect calls // (when R_MIPS_CALL* is not used for the call) because Mips linker generates // lazy binding stub for a function only when R_MIPS_CALL* are the only relocs // used for the function (that is, Mips linker doesn't generate lazy binding // stub for a function whose address is taken in the program). if (IsPICCall && !InternalLinkage && IsCallReloc) { unsigned GPReg = ABI.IsN64() ? Mips::GP_64 : Mips::GP; EVT Ty = ABI.IsN64() ? MVT::i64 : MVT::i32; RegsToPass.push_back(std::make_pair(GPReg, getGlobalReg(CLI.DAG, Ty))); } // Build a sequence of copy-to-reg nodes chained together with token // chain and flag operands which copy the outgoing args into registers. // The InFlag in necessary since all emitted instructions must be // stuck together. SDValue InFlag; for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) { Chain = CLI.DAG.getCopyToReg(Chain, CLI.DL, RegsToPass[i].first, RegsToPass[i].second, InFlag); InFlag = Chain.getValue(1); } // Add argument registers to the end of the list so that they are // known live into the call. for (unsigned i = 0, e = RegsToPass.size(); i != e; ++i) Ops.push_back(CLI.DAG.getRegister(RegsToPass[i].first, RegsToPass[i].second.getValueType())); // Add a register mask operand representing the call-preserved registers. const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo(); const uint32_t *Mask = TRI->getCallPreservedMask(CLI.DAG.getMachineFunction(), CLI.CallConv); assert(Mask && "Missing call preserved mask for calling convention"); if (Subtarget.inMips16HardFloat()) { if (GlobalAddressSDNode *G = dyn_cast(CLI.Callee)) { StringRef Sym = G->getGlobal()->getName(); Function *F = G->getGlobal()->getParent()->getFunction(Sym); if (F && F->hasFnAttribute("__Mips16RetHelper")) { Mask = MipsRegisterInfo::getMips16RetHelperMask(); } } } Ops.push_back(CLI.DAG.getRegisterMask(Mask)); if (InFlag.getNode()) Ops.push_back(InFlag); } /// LowerCall - functions arguments are copied from virtual regs to /// (physical regs)/(stack frame), CALLSEQ_START and CALLSEQ_END are emitted. SDValue MipsTargetLowering::LowerCall(TargetLowering::CallLoweringInfo &CLI, SmallVectorImpl &InVals) const { SelectionDAG &DAG = CLI.DAG; SDLoc DL = CLI.DL; SmallVectorImpl &Outs = CLI.Outs; SmallVectorImpl &OutVals = CLI.OutVals; SmallVectorImpl &Ins = CLI.Ins; SDValue Chain = CLI.Chain; SDValue Callee = CLI.Callee; bool &IsTailCall = CLI.IsTailCall; CallingConv::ID CallConv = CLI.CallConv; bool IsVarArg = CLI.IsVarArg; MachineFunction &MF = DAG.getMachineFunction(); MachineFrameInfo &MFI = MF.getFrameInfo(); const TargetFrameLowering *TFL = Subtarget.getFrameLowering(); MipsFunctionInfo *FuncInfo = MF.getInfo(); bool IsPIC = isPositionIndependent(); // Analyze operands of the call, assigning locations to each operand. SmallVector ArgLocs; MipsCCState CCInfo( CallConv, IsVarArg, DAG.getMachineFunction(), ArgLocs, *DAG.getContext(), MipsCCState::getSpecialCallingConvForCallee(Callee.getNode(), Subtarget)); const ExternalSymbolSDNode *ES = dyn_cast_or_null(Callee.getNode()); // There is one case where CALLSEQ_START..CALLSEQ_END can be nested, which // is during the lowering of a call with a byval argument which produces // a call to memcpy. For the O32 case, this causes the caller to allocate // stack space for the reserved argument area for the callee, then recursively // again for the memcpy call. In the NEWABI case, this doesn't occur as those // ABIs mandate that the callee allocates the reserved argument area. We do // still produce nested CALLSEQ_START..CALLSEQ_END with zero space though. // // If the callee has a byval argument and memcpy is used, we are mandated // to already have produced a reserved argument area for the callee for O32. // Therefore, the reserved argument area can be reused for both calls. // // Other cases of calling memcpy cannot have a chain with a CALLSEQ_START // present, as we have yet to hook that node onto the chain. // // Hence, the CALLSEQ_START and CALLSEQ_END nodes can be eliminated in this // case. GCC does a similar trick, in that wherever possible, it calculates // the maximum out going argument area (including the reserved area), and // preallocates the stack space on entrance to the caller. // // FIXME: We should do the same for efficency and space. // Note: The check on the calling convention below must match // MipsABIInfo::GetCalleeAllocdArgSizeInBytes(). bool MemcpyInByVal = ES && StringRef(ES->getSymbol()) == StringRef("memcpy") && CallConv != CallingConv::Fast && Chain.getOpcode() == ISD::CALLSEQ_START; // Allocate the reserved argument area. It seems strange to do this from the // caller side but removing it breaks the frame size calculation. unsigned ReservedArgArea = MemcpyInByVal ? 0 : ABI.GetCalleeAllocdArgSizeInBytes(CallConv); CCInfo.AllocateStack(ReservedArgArea, 1); CCInfo.AnalyzeCallOperands(Outs, CC_Mips, CLI.getArgs(), ES ? ES->getSymbol() : nullptr); // Get a count of how many bytes are to be pushed on the stack. unsigned NextStackOffset = CCInfo.getNextStackOffset(); // Check if it's really possible to do a tail call. Restrict it to functions // that are part of this compilation unit. bool InternalLinkage = false; if (IsTailCall) { IsTailCall = isEligibleForTailCallOptimization( CCInfo, NextStackOffset, *MF.getInfo()); if (GlobalAddressSDNode *G = dyn_cast(Callee)) { InternalLinkage = G->getGlobal()->hasInternalLinkage(); IsTailCall &= (InternalLinkage || G->getGlobal()->hasLocalLinkage() || G->getGlobal()->hasPrivateLinkage() || G->getGlobal()->hasHiddenVisibility() || G->getGlobal()->hasProtectedVisibility()); } } if (!IsTailCall && CLI.CS && CLI.CS.isMustTailCall()) report_fatal_error("failed to perform tail call elimination on a call " "site marked musttail"); if (IsTailCall) ++NumTailCalls; // Chain is the output chain of the last Load/Store or CopyToReg node. // ByValChain is the output chain of the last Memcpy node created for copying // byval arguments to the stack. unsigned StackAlignment = TFL->getStackAlignment(); NextStackOffset = alignTo(NextStackOffset, StackAlignment); SDValue NextStackOffsetVal = DAG.getIntPtrConstant(NextStackOffset, DL, true); if (!(IsTailCall || MemcpyInByVal)) Chain = DAG.getCALLSEQ_START(Chain, NextStackOffset, 0, DL); SDValue StackPtr = DAG.getCopyFromReg(Chain, DL, ABI.IsN64() ? Mips::SP_64 : Mips::SP, getPointerTy(DAG.getDataLayout())); std::deque> RegsToPass; SmallVector MemOpChains; CCInfo.rewindByValRegsInfo(); // Walk the register/memloc assignments, inserting copies/loads. for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { SDValue Arg = OutVals[i]; CCValAssign &VA = ArgLocs[i]; MVT ValVT = VA.getValVT(), LocVT = VA.getLocVT(); ISD::ArgFlagsTy Flags = Outs[i].Flags; bool UseUpperBits = false; // ByVal Arg. if (Flags.isByVal()) { unsigned FirstByValReg, LastByValReg; unsigned ByValIdx = CCInfo.getInRegsParamsProcessed(); CCInfo.getInRegsParamInfo(ByValIdx, FirstByValReg, LastByValReg); assert(Flags.getByValSize() && "ByVal args of size 0 should have been ignored by front-end."); assert(ByValIdx < CCInfo.getInRegsParamsCount()); assert(!IsTailCall && "Do not tail-call optimize if there is a byval argument."); passByValArg(Chain, DL, RegsToPass, MemOpChains, StackPtr, MFI, DAG, Arg, FirstByValReg, LastByValReg, Flags, Subtarget.isLittle(), VA); CCInfo.nextInRegsParam(); continue; } // Promote the value if needed. switch (VA.getLocInfo()) { default: llvm_unreachable("Unknown loc info!"); case CCValAssign::Full: if (VA.isRegLoc()) { if ((ValVT == MVT::f32 && LocVT == MVT::i32) || (ValVT == MVT::f64 && LocVT == MVT::i64) || (ValVT == MVT::i64 && LocVT == MVT::f64)) Arg = DAG.getNode(ISD::BITCAST, DL, LocVT, Arg); else if (ValVT == MVT::f64 && LocVT == MVT::i32) { SDValue Lo = DAG.getNode(MipsISD::ExtractElementF64, DL, MVT::i32, Arg, DAG.getConstant(0, DL, MVT::i32)); SDValue Hi = DAG.getNode(MipsISD::ExtractElementF64, DL, MVT::i32, Arg, DAG.getConstant(1, DL, MVT::i32)); if (!Subtarget.isLittle()) std::swap(Lo, Hi); unsigned LocRegLo = VA.getLocReg(); unsigned LocRegHigh = getNextIntArgReg(LocRegLo); RegsToPass.push_back(std::make_pair(LocRegLo, Lo)); RegsToPass.push_back(std::make_pair(LocRegHigh, Hi)); continue; } } break; case CCValAssign::BCvt: Arg = DAG.getNode(ISD::BITCAST, DL, LocVT, Arg); break; case CCValAssign::SExtUpper: UseUpperBits = true; LLVM_FALLTHROUGH; case CCValAssign::SExt: Arg = DAG.getNode(ISD::SIGN_EXTEND, DL, LocVT, Arg); break; case CCValAssign::ZExtUpper: UseUpperBits = true; LLVM_FALLTHROUGH; case CCValAssign::ZExt: Arg = DAG.getNode(ISD::ZERO_EXTEND, DL, LocVT, Arg); break; case CCValAssign::AExtUpper: UseUpperBits = true; LLVM_FALLTHROUGH; case CCValAssign::AExt: Arg = DAG.getNode(ISD::ANY_EXTEND, DL, LocVT, Arg); break; } if (UseUpperBits) { unsigned ValSizeInBits = Outs[i].ArgVT.getSizeInBits(); unsigned LocSizeInBits = VA.getLocVT().getSizeInBits(); Arg = DAG.getNode( ISD::SHL, DL, VA.getLocVT(), Arg, DAG.getConstant(LocSizeInBits - ValSizeInBits, DL, VA.getLocVT())); } // Arguments that can be passed on register must be kept at // RegsToPass vector if (VA.isRegLoc()) { RegsToPass.push_back(std::make_pair(VA.getLocReg(), Arg)); continue; } // Register can't get to this point... assert(VA.isMemLoc()); // emit ISD::STORE whichs stores the // parameter value to a stack Location MemOpChains.push_back(passArgOnStack(StackPtr, VA.getLocMemOffset(), Chain, Arg, DL, IsTailCall, DAG)); } // Transform all store nodes into one single node because all store // nodes are independent of each other. if (!MemOpChains.empty()) Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, MemOpChains); // If the callee is a GlobalAddress/ExternalSymbol node (quite common, every // direct call is) turn it into a TargetGlobalAddress/TargetExternalSymbol // node so that legalize doesn't hack it. EVT Ty = Callee.getValueType(); bool GlobalOrExternal = false, IsCallReloc = false; // The long-calls feature is ignored in case of PIC. // While we do not support -mshared / -mno-shared properly, // ignore long-calls in case of -mabicalls too. if (!Subtarget.isABICalls() && !IsPIC) { // If the function should be called using "long call", // get its address into a register to prevent using // of the `jal` instruction for the direct call. if (auto *N = dyn_cast(Callee)) { if (Subtarget.useLongCalls()) Callee = Subtarget.hasSym32() ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); } else if (auto *N = dyn_cast(Callee)) { bool UseLongCalls = Subtarget.useLongCalls(); // If the function has long-call/far/near attribute // it overrides command line switch pased to the backend. if (auto *F = dyn_cast(N->getGlobal())) { if (F->hasFnAttribute("long-call")) UseLongCalls = true; else if (F->hasFnAttribute("short-call")) UseLongCalls = false; } if (UseLongCalls) Callee = Subtarget.hasSym32() ? getAddrNonPIC(N, SDLoc(N), Ty, DAG) : getAddrNonPICSym64(N, SDLoc(N), Ty, DAG); } } if (GlobalAddressSDNode *G = dyn_cast(Callee)) { if (IsPIC) { const GlobalValue *Val = G->getGlobal(); InternalLinkage = Val->hasInternalLinkage(); if (InternalLinkage) Callee = getAddrLocal(G, DL, Ty, DAG, ABI.IsN32() || ABI.IsN64()); else if (LargeGOT) { Callee = getAddrGlobalLargeGOT(G, DL, Ty, DAG, MipsII::MO_CALL_HI16, MipsII::MO_CALL_LO16, Chain, FuncInfo->callPtrInfo(Val)); IsCallReloc = true; } else { Callee = getAddrGlobal(G, DL, Ty, DAG, MipsII::MO_GOT_CALL, Chain, FuncInfo->callPtrInfo(Val)); IsCallReloc = true; } } else Callee = DAG.getTargetGlobalAddress(G->getGlobal(), DL, getPointerTy(DAG.getDataLayout()), 0, MipsII::MO_NO_FLAG); GlobalOrExternal = true; } else if (ExternalSymbolSDNode *S = dyn_cast(Callee)) { const char *Sym = S->getSymbol(); if (!IsPIC) // static Callee = DAG.getTargetExternalSymbol( Sym, getPointerTy(DAG.getDataLayout()), MipsII::MO_NO_FLAG); else if (LargeGOT) { Callee = getAddrGlobalLargeGOT(S, DL, Ty, DAG, MipsII::MO_CALL_HI16, MipsII::MO_CALL_LO16, Chain, FuncInfo->callPtrInfo(Sym)); IsCallReloc = true; } else { // PIC Callee = getAddrGlobal(S, DL, Ty, DAG, MipsII::MO_GOT_CALL, Chain, FuncInfo->callPtrInfo(Sym)); IsCallReloc = true; } GlobalOrExternal = true; } SmallVector Ops(1, Chain); SDVTList NodeTys = DAG.getVTList(MVT::Other, MVT::Glue); getOpndList(Ops, RegsToPass, IsPIC, GlobalOrExternal, InternalLinkage, IsCallReloc, CLI, Callee, Chain); if (IsTailCall) { MF.getFrameInfo().setHasTailCall(); return DAG.getNode(MipsISD::TailCall, DL, MVT::Other, Ops); } Chain = DAG.getNode(MipsISD::JmpLink, DL, NodeTys, Ops); SDValue InFlag = Chain.getValue(1); // Create the CALLSEQ_END node in the case of where it is not a call to // memcpy. if (!(MemcpyInByVal)) { Chain = DAG.getCALLSEQ_END(Chain, NextStackOffsetVal, DAG.getIntPtrConstant(0, DL, true), InFlag, DL); InFlag = Chain.getValue(1); } // Handle result values, copying them out of physregs into vregs that we // return. return LowerCallResult(Chain, InFlag, CallConv, IsVarArg, Ins, DL, DAG, InVals, CLI); } /// LowerCallResult - Lower the result values of a call into the /// appropriate copies out of appropriate physical registers. SDValue MipsTargetLowering::LowerCallResult( SDValue Chain, SDValue InFlag, CallingConv::ID CallConv, bool IsVarArg, const SmallVectorImpl &Ins, const SDLoc &DL, SelectionDAG &DAG, SmallVectorImpl &InVals, TargetLowering::CallLoweringInfo &CLI) const { // Assign locations to each value returned by this call. SmallVector RVLocs; MipsCCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), RVLocs, *DAG.getContext()); const ExternalSymbolSDNode *ES = dyn_cast_or_null(CLI.Callee.getNode()); CCInfo.AnalyzeCallResult(Ins, RetCC_Mips, CLI.RetTy, ES ? ES->getSymbol() : nullptr); // Copy all of the result registers out of their specified physreg. for (unsigned i = 0; i != RVLocs.size(); ++i) { CCValAssign &VA = RVLocs[i]; assert(VA.isRegLoc() && "Can only return in registers!"); SDValue Val = DAG.getCopyFromReg(Chain, DL, RVLocs[i].getLocReg(), RVLocs[i].getLocVT(), InFlag); Chain = Val.getValue(1); InFlag = Val.getValue(2); if (VA.isUpperBitsInLoc()) { unsigned ValSizeInBits = Ins[i].ArgVT.getSizeInBits(); unsigned LocSizeInBits = VA.getLocVT().getSizeInBits(); unsigned Shift = VA.getLocInfo() == CCValAssign::ZExtUpper ? ISD::SRL : ISD::SRA; Val = DAG.getNode( Shift, DL, VA.getLocVT(), Val, DAG.getConstant(LocSizeInBits - ValSizeInBits, DL, VA.getLocVT())); } switch (VA.getLocInfo()) { default: llvm_unreachable("Unknown loc info!"); case CCValAssign::Full: break; case CCValAssign::BCvt: Val = DAG.getNode(ISD::BITCAST, DL, VA.getValVT(), Val); break; case CCValAssign::AExt: case CCValAssign::AExtUpper: Val = DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), Val); break; case CCValAssign::ZExt: case CCValAssign::ZExtUpper: Val = DAG.getNode(ISD::AssertZext, DL, VA.getLocVT(), Val, DAG.getValueType(VA.getValVT())); Val = DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), Val); break; case CCValAssign::SExt: case CCValAssign::SExtUpper: Val = DAG.getNode(ISD::AssertSext, DL, VA.getLocVT(), Val, DAG.getValueType(VA.getValVT())); Val = DAG.getNode(ISD::TRUNCATE, DL, VA.getValVT(), Val); break; } InVals.push_back(Val); } return Chain; } static SDValue UnpackFromArgumentSlot(SDValue Val, const CCValAssign &VA, EVT ArgVT, const SDLoc &DL, SelectionDAG &DAG) { MVT LocVT = VA.getLocVT(); EVT ValVT = VA.getValVT(); // Shift into the upper bits if necessary. switch (VA.getLocInfo()) { default: break; case CCValAssign::AExtUpper: case CCValAssign::SExtUpper: case CCValAssign::ZExtUpper: { unsigned ValSizeInBits = ArgVT.getSizeInBits(); unsigned LocSizeInBits = VA.getLocVT().getSizeInBits(); unsigned Opcode = VA.getLocInfo() == CCValAssign::ZExtUpper ? ISD::SRL : ISD::SRA; Val = DAG.getNode( Opcode, DL, VA.getLocVT(), Val, DAG.getConstant(LocSizeInBits - ValSizeInBits, DL, VA.getLocVT())); break; } } // If this is an value smaller than the argument slot size (32-bit for O32, // 64-bit for N32/N64), it has been promoted in some way to the argument slot // size. Extract the value and insert any appropriate assertions regarding // sign/zero extension. switch (VA.getLocInfo()) { default: llvm_unreachable("Unknown loc info!"); case CCValAssign::Full: break; case CCValAssign::AExtUpper: case CCValAssign::AExt: Val = DAG.getNode(ISD::TRUNCATE, DL, ValVT, Val); break; case CCValAssign::SExtUpper: case CCValAssign::SExt: Val = DAG.getNode(ISD::AssertSext, DL, LocVT, Val, DAG.getValueType(ValVT)); Val = DAG.getNode(ISD::TRUNCATE, DL, ValVT, Val); break; case CCValAssign::ZExtUpper: case CCValAssign::ZExt: Val = DAG.getNode(ISD::AssertZext, DL, LocVT, Val, DAG.getValueType(ValVT)); Val = DAG.getNode(ISD::TRUNCATE, DL, ValVT, Val); break; case CCValAssign::BCvt: Val = DAG.getNode(ISD::BITCAST, DL, ValVT, Val); break; } return Val; } //===----------------------------------------------------------------------===// // Formal Arguments Calling Convention Implementation //===----------------------------------------------------------------------===// /// LowerFormalArguments - transform physical registers into virtual registers /// and generate load operations for arguments places on the stack. SDValue MipsTargetLowering::LowerFormalArguments( SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, const SmallVectorImpl &Ins, const SDLoc &DL, SelectionDAG &DAG, SmallVectorImpl &InVals) const { MachineFunction &MF = DAG.getMachineFunction(); MachineFrameInfo &MFI = MF.getFrameInfo(); MipsFunctionInfo *MipsFI = MF.getInfo(); MipsFI->setVarArgsFrameIndex(0); // Used with vargs to acumulate store chains. std::vector OutChains; // Assign locations to all of the incoming arguments. SmallVector ArgLocs; MipsCCState CCInfo(CallConv, IsVarArg, DAG.getMachineFunction(), ArgLocs, *DAG.getContext()); CCInfo.AllocateStack(ABI.GetCalleeAllocdArgSizeInBytes(CallConv), 1); const Function &Func = DAG.getMachineFunction().getFunction(); Function::const_arg_iterator FuncArg = Func.arg_begin(); if (Func.hasFnAttribute("interrupt") && !Func.arg_empty()) report_fatal_error( "Functions with the interrupt attribute cannot have arguments!"); CCInfo.AnalyzeFormalArguments(Ins, CC_Mips_FixedArg); MipsFI->setFormalArgInfo(CCInfo.getNextStackOffset(), CCInfo.getInRegsParamsCount() > 0); unsigned CurArgIdx = 0; CCInfo.rewindByValRegsInfo(); for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { CCValAssign &VA = ArgLocs[i]; if (Ins[i].isOrigArg()) { std::advance(FuncArg, Ins[i].getOrigArgIndex() - CurArgIdx); CurArgIdx = Ins[i].getOrigArgIndex(); } EVT ValVT = VA.getValVT(); ISD::ArgFlagsTy Flags = Ins[i].Flags; bool IsRegLoc = VA.isRegLoc(); if (Flags.isByVal()) { assert(Ins[i].isOrigArg() && "Byval arguments cannot be implicit"); unsigned FirstByValReg, LastByValReg; unsigned ByValIdx = CCInfo.getInRegsParamsProcessed(); CCInfo.getInRegsParamInfo(ByValIdx, FirstByValReg, LastByValReg); assert(Flags.getByValSize() && "ByVal args of size 0 should have been ignored by front-end."); assert(ByValIdx < CCInfo.getInRegsParamsCount()); copyByValRegs(Chain, DL, OutChains, DAG, Flags, InVals, &*FuncArg, FirstByValReg, LastByValReg, VA, CCInfo); CCInfo.nextInRegsParam(); continue; } // Arguments stored on registers if (IsRegLoc) { MVT RegVT = VA.getLocVT(); unsigned ArgReg = VA.getLocReg(); const TargetRegisterClass *RC = getRegClassFor(RegVT); // Transform the arguments stored on // physical registers into virtual ones unsigned Reg = addLiveIn(DAG.getMachineFunction(), ArgReg, RC); SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, RegVT); ArgValue = UnpackFromArgumentSlot(ArgValue, VA, Ins[i].ArgVT, DL, DAG); // Handle floating point arguments passed in integer registers and // long double arguments passed in floating point registers. if ((RegVT == MVT::i32 && ValVT == MVT::f32) || (RegVT == MVT::i64 && ValVT == MVT::f64) || (RegVT == MVT::f64 && ValVT == MVT::i64)) ArgValue = DAG.getNode(ISD::BITCAST, DL, ValVT, ArgValue); else if (ABI.IsO32() && RegVT == MVT::i32 && ValVT == MVT::f64) { unsigned Reg2 = addLiveIn(DAG.getMachineFunction(), getNextIntArgReg(ArgReg), RC); SDValue ArgValue2 = DAG.getCopyFromReg(Chain, DL, Reg2, RegVT); if (!Subtarget.isLittle()) std::swap(ArgValue, ArgValue2); ArgValue = DAG.getNode(MipsISD::BuildPairF64, DL, MVT::f64, ArgValue, ArgValue2); } InVals.push_back(ArgValue); } else { // VA.isRegLoc() MVT LocVT = VA.getLocVT(); if (ABI.IsO32()) { // We ought to be able to use LocVT directly but O32 sets it to i32 // when allocating floating point values to integer registers. // This shouldn't influence how we load the value into registers unless // we are targeting softfloat. if (VA.getValVT().isFloatingPoint() && !Subtarget.useSoftFloat()) LocVT = VA.getValVT(); } // sanity check assert(VA.isMemLoc()); // The stack pointer offset is relative to the caller stack frame. int FI = MFI.CreateFixedObject(LocVT.getSizeInBits() / 8, VA.getLocMemOffset(), true); // Create load nodes to retrieve arguments from the stack SDValue FIN = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); SDValue ArgValue = DAG.getLoad( LocVT, DL, Chain, FIN, MachinePointerInfo::getFixedStack(DAG.getMachineFunction(), FI)); OutChains.push_back(ArgValue.getValue(1)); ArgValue = UnpackFromArgumentSlot(ArgValue, VA, Ins[i].ArgVT, DL, DAG); InVals.push_back(ArgValue); } } for (unsigned i = 0, e = ArgLocs.size(); i != e; ++i) { // The mips ABIs for returning structs by value requires that we copy // the sret argument into $v0 for the return. Save the argument into // a virtual register so that we can access it from the return points. if (Ins[i].Flags.isSRet()) { unsigned Reg = MipsFI->getSRetReturnReg(); if (!Reg) { Reg = MF.getRegInfo().createVirtualRegister( getRegClassFor(ABI.IsN64() ? MVT::i64 : MVT::i32)); MipsFI->setSRetReturnReg(Reg); } SDValue Copy = DAG.getCopyToReg(DAG.getEntryNode(), DL, Reg, InVals[i]); Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, Copy, Chain); break; } } if (IsVarArg) writeVarArgRegs(OutChains, Chain, DL, DAG, CCInfo); // All stores are grouped in one node to allow the matching between // the size of Ins and InVals. This only happens when on varg functions if (!OutChains.empty()) { OutChains.push_back(Chain); Chain = DAG.getNode(ISD::TokenFactor, DL, MVT::Other, OutChains); } return Chain; } //===----------------------------------------------------------------------===// // Return Value Calling Convention Implementation //===----------------------------------------------------------------------===// bool MipsTargetLowering::CanLowerReturn(CallingConv::ID CallConv, MachineFunction &MF, bool IsVarArg, const SmallVectorImpl &Outs, LLVMContext &Context) const { SmallVector RVLocs; MipsCCState CCInfo(CallConv, IsVarArg, MF, RVLocs, Context); return CCInfo.CheckReturn(Outs, RetCC_Mips); } bool MipsTargetLowering::shouldSignExtendTypeInLibCall(EVT Type, bool IsSigned) const { if ((ABI.IsN32() || ABI.IsN64()) && Type == MVT::i32) return true; return IsSigned; } SDValue MipsTargetLowering::LowerInterruptReturn(SmallVectorImpl &RetOps, const SDLoc &DL, SelectionDAG &DAG) const { MachineFunction &MF = DAG.getMachineFunction(); MipsFunctionInfo *MipsFI = MF.getInfo(); MipsFI->setISR(); return DAG.getNode(MipsISD::ERet, DL, MVT::Other, RetOps); } SDValue MipsTargetLowering::LowerReturn(SDValue Chain, CallingConv::ID CallConv, bool IsVarArg, const SmallVectorImpl &Outs, const SmallVectorImpl &OutVals, const SDLoc &DL, SelectionDAG &DAG) const { // CCValAssign - represent the assignment of // the return value to a location SmallVector RVLocs; MachineFunction &MF = DAG.getMachineFunction(); // CCState - Info about the registers and stack slot. MipsCCState CCInfo(CallConv, IsVarArg, MF, RVLocs, *DAG.getContext()); // Analyze return values. CCInfo.AnalyzeReturn(Outs, RetCC_Mips); SDValue Flag; SmallVector RetOps(1, Chain); // Copy the result values into the output registers. for (unsigned i = 0; i != RVLocs.size(); ++i) { SDValue Val = OutVals[i]; CCValAssign &VA = RVLocs[i]; assert(VA.isRegLoc() && "Can only return in registers!"); bool UseUpperBits = false; switch (VA.getLocInfo()) { default: llvm_unreachable("Unknown loc info!"); case CCValAssign::Full: break; case CCValAssign::BCvt: Val = DAG.getNode(ISD::BITCAST, DL, VA.getLocVT(), Val); break; case CCValAssign::AExtUpper: UseUpperBits = true; LLVM_FALLTHROUGH; case CCValAssign::AExt: Val = DAG.getNode(ISD::ANY_EXTEND, DL, VA.getLocVT(), Val); break; case CCValAssign::ZExtUpper: UseUpperBits = true; LLVM_FALLTHROUGH; case CCValAssign::ZExt: Val = DAG.getNode(ISD::ZERO_EXTEND, DL, VA.getLocVT(), Val); break; case CCValAssign::SExtUpper: UseUpperBits = true; LLVM_FALLTHROUGH; case CCValAssign::SExt: Val = DAG.getNode(ISD::SIGN_EXTEND, DL, VA.getLocVT(), Val); break; } if (UseUpperBits) { unsigned ValSizeInBits = Outs[i].ArgVT.getSizeInBits(); unsigned LocSizeInBits = VA.getLocVT().getSizeInBits(); Val = DAG.getNode( ISD::SHL, DL, VA.getLocVT(), Val, DAG.getConstant(LocSizeInBits - ValSizeInBits, DL, VA.getLocVT())); } Chain = DAG.getCopyToReg(Chain, DL, VA.getLocReg(), Val, Flag); // Guarantee that all emitted copies are stuck together with flags. Flag = Chain.getValue(1); RetOps.push_back(DAG.getRegister(VA.getLocReg(), VA.getLocVT())); } // The mips ABIs for returning structs by value requires that we copy // the sret argument into $v0 for the return. We saved the argument into // a virtual register in the entry block, so now we copy the value out // and into $v0. if (MF.getFunction().hasStructRetAttr()) { MipsFunctionInfo *MipsFI = MF.getInfo(); unsigned Reg = MipsFI->getSRetReturnReg(); if (!Reg) llvm_unreachable("sret virtual register not created in the entry block"); SDValue Val = DAG.getCopyFromReg(Chain, DL, Reg, getPointerTy(DAG.getDataLayout())); unsigned V0 = ABI.IsN64() ? Mips::V0_64 : Mips::V0; Chain = DAG.getCopyToReg(Chain, DL, V0, Val, Flag); Flag = Chain.getValue(1); RetOps.push_back(DAG.getRegister(V0, getPointerTy(DAG.getDataLayout()))); } RetOps[0] = Chain; // Update chain. // Add the flag if we have it. if (Flag.getNode()) RetOps.push_back(Flag); // ISRs must use "eret". if (DAG.getMachineFunction().getFunction().hasFnAttribute("interrupt")) return LowerInterruptReturn(RetOps, DL, DAG); // Standard return on Mips is a "jr $ra" return DAG.getNode(MipsISD::Ret, DL, MVT::Other, RetOps); } //===----------------------------------------------------------------------===// // Mips Inline Assembly Support //===----------------------------------------------------------------------===// /// getConstraintType - Given a constraint letter, return the type of /// constraint it is for this target. MipsTargetLowering::ConstraintType MipsTargetLowering::getConstraintType(StringRef Constraint) const { // Mips specific constraints // GCC config/mips/constraints.md // // 'd' : An address register. Equivalent to r // unless generating MIPS16 code. // 'y' : Equivalent to r; retained for // backwards compatibility. // 'c' : A register suitable for use in an indirect // jump. This will always be $25 for -mabicalls. // 'l' : The lo register. 1 word storage. // 'x' : The hilo register pair. Double word storage. if (Constraint.size() == 1) { switch (Constraint[0]) { default : break; case 'd': case 'y': case 'f': case 'c': case 'l': case 'x': return C_RegisterClass; case 'R': return C_Memory; } } if (Constraint == "ZC") return C_Memory; return TargetLowering::getConstraintType(Constraint); } /// Examine constraint type and operand type and determine a weight value. /// This object must already have been set up with the operand type /// and the current alternative constraint selected. TargetLowering::ConstraintWeight MipsTargetLowering::getSingleConstraintMatchWeight( AsmOperandInfo &info, const char *constraint) const { ConstraintWeight weight = CW_Invalid; Value *CallOperandVal = info.CallOperandVal; // If we don't have a value, we can't do a match, // but allow it at the lowest weight. if (!CallOperandVal) return CW_Default; Type *type = CallOperandVal->getType(); // Look at the constraint type. switch (*constraint) { default: weight = TargetLowering::getSingleConstraintMatchWeight(info, constraint); break; case 'd': case 'y': if (type->isIntegerTy()) weight = CW_Register; break; case 'f': // FPU or MSA register if (Subtarget.hasMSA() && type->isVectorTy() && cast(type)->getBitWidth() == 128) weight = CW_Register; else if (type->isFloatTy()) weight = CW_Register; break; case 'c': // $25 for indirect jumps case 'l': // lo register case 'x': // hilo register pair if (type->isIntegerTy()) weight = CW_SpecificReg; break; case 'I': // signed 16 bit immediate case 'J': // integer zero case 'K': // unsigned 16 bit immediate case 'L': // signed 32 bit immediate where lower 16 bits are 0 case 'N': // immediate in the range of -65535 to -1 (inclusive) case 'O': // signed 15 bit immediate (+- 16383) case 'P': // immediate in the range of 65535 to 1 (inclusive) if (isa(CallOperandVal)) weight = CW_Constant; break; case 'R': weight = CW_Memory; break; } return weight; } /// This is a helper function to parse a physical register string and split it /// into non-numeric and numeric parts (Prefix and Reg). The first boolean flag /// that is returned indicates whether parsing was successful. The second flag /// is true if the numeric part exists. static std::pair parsePhysicalReg(StringRef C, StringRef &Prefix, unsigned long long &Reg) { if (C.front() != '{' || C.back() != '}') return std::make_pair(false, false); // Search for the first numeric character. StringRef::const_iterator I, B = C.begin() + 1, E = C.end() - 1; I = std::find_if(B, E, isdigit); Prefix = StringRef(B, I - B); // The second flag is set to false if no numeric characters were found. if (I == E) return std::make_pair(true, false); // Parse the numeric characters. return std::make_pair(!getAsUnsignedInteger(StringRef(I, E - I), 10, Reg), true); } std::pair MipsTargetLowering:: parseRegForInlineAsmConstraint(StringRef C, MVT VT) const { const TargetRegisterInfo *TRI = Subtarget.getRegisterInfo(); const TargetRegisterClass *RC; StringRef Prefix; unsigned long long Reg; std::pair R = parsePhysicalReg(C, Prefix, Reg); if (!R.first) return std::make_pair(0U, nullptr); if ((Prefix == "hi" || Prefix == "lo")) { // Parse hi/lo. // No numeric characters follow "hi" or "lo". if (R.second) return std::make_pair(0U, nullptr); RC = TRI->getRegClass(Prefix == "hi" ? Mips::HI32RegClassID : Mips::LO32RegClassID); return std::make_pair(*(RC->begin()), RC); } else if (Prefix.startswith("$msa")) { // Parse $msa(ir|csr|access|save|modify|request|map|unmap) // No numeric characters follow the name. if (R.second) return std::make_pair(0U, nullptr); Reg = StringSwitch(Prefix) .Case("$msair", Mips::MSAIR) .Case("$msacsr", Mips::MSACSR) .Case("$msaaccess", Mips::MSAAccess) .Case("$msasave", Mips::MSASave) .Case("$msamodify", Mips::MSAModify) .Case("$msarequest", Mips::MSARequest) .Case("$msamap", Mips::MSAMap) .Case("$msaunmap", Mips::MSAUnmap) .Default(0); if (!Reg) return std::make_pair(0U, nullptr); RC = TRI->getRegClass(Mips::MSACtrlRegClassID); return std::make_pair(Reg, RC); } if (!R.second) return std::make_pair(0U, nullptr); if (Prefix == "$f") { // Parse $f0-$f31. // If the size of FP registers is 64-bit or Reg is an even number, select // the 64-bit register class. Otherwise, select the 32-bit register class. if (VT == MVT::Other) VT = (Subtarget.isFP64bit() || !(Reg % 2)) ? MVT::f64 : MVT::f32; RC = getRegClassFor(VT); if (RC == &Mips::AFGR64RegClass) { assert(Reg % 2 == 0); Reg >>= 1; } } else if (Prefix == "$fcc") // Parse $fcc0-$fcc7. RC = TRI->getRegClass(Mips::FCCRegClassID); else if (Prefix == "$w") { // Parse $w0-$w31. RC = getRegClassFor((VT == MVT::Other) ? MVT::v16i8 : VT); } else { // Parse $0-$31. assert(Prefix == "$"); RC = getRegClassFor((VT == MVT::Other) ? MVT::i32 : VT); } assert(Reg < RC->getNumRegs()); return std::make_pair(*(RC->begin() + Reg), RC); } /// Given a register class constraint, like 'r', if this corresponds directly /// to an LLVM register class, return a register of 0 and the register class /// pointer. std::pair MipsTargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *TRI, StringRef Constraint, MVT VT) const { if (Constraint.size() == 1) { switch (Constraint[0]) { case 'd': // Address register. Same as 'r' unless generating MIPS16 code. case 'y': // Same as 'r'. Exists for compatibility. case 'r': if (VT == MVT::i32 || VT == MVT::i16 || VT == MVT::i8) { if (Subtarget.inMips16Mode()) return std::make_pair(0U, &Mips::CPU16RegsRegClass); return std::make_pair(0U, &Mips::GPR32RegClass); } if (VT == MVT::i64 && !Subtarget.isGP64bit()) return std::make_pair(0U, &Mips::GPR32RegClass); if (VT == MVT::i64 && Subtarget.isGP64bit()) return std::make_pair(0U, &Mips::GPR64RegClass); // This will generate an error message return std::make_pair(0U, nullptr); case 'f': // FPU or MSA register if (VT == MVT::v16i8) return std::make_pair(0U, &Mips::MSA128BRegClass); else if (VT == MVT::v8i16 || VT == MVT::v8f16) return std::make_pair(0U, &Mips::MSA128HRegClass); else if (VT == MVT::v4i32 || VT == MVT::v4f32) return std::make_pair(0U, &Mips::MSA128WRegClass); else if (VT == MVT::v2i64 || VT == MVT::v2f64) return std::make_pair(0U, &Mips::MSA128DRegClass); else if (VT == MVT::f32) return std::make_pair(0U, &Mips::FGR32RegClass); else if ((VT == MVT::f64) && (!Subtarget.isSingleFloat())) { if (Subtarget.isFP64bit()) return std::make_pair(0U, &Mips::FGR64RegClass); return std::make_pair(0U, &Mips::AFGR64RegClass); } break; case 'c': // register suitable for indirect jump if (VT == MVT::i32) return std::make_pair((unsigned)Mips::T9, &Mips::GPR32RegClass); if (VT == MVT::i64) return std::make_pair((unsigned)Mips::T9_64, &Mips::GPR64RegClass); // This will generate an error message return std::make_pair(0U, nullptr); case 'l': // use the `lo` register to store values // that are no bigger than a word if (VT == MVT::i32 || VT == MVT::i16 || VT == MVT::i8) return std::make_pair((unsigned)Mips::LO0, &Mips::LO32RegClass); return std::make_pair((unsigned)Mips::LO0_64, &Mips::LO64RegClass); case 'x': // use the concatenated `hi` and `lo` registers // to store doubleword values // Fixme: Not triggering the use of both hi and low // This will generate an error message return std::make_pair(0U, nullptr); } } std::pair R; R = parseRegForInlineAsmConstraint(Constraint, VT); if (R.second) return R; return TargetLowering::getRegForInlineAsmConstraint(TRI, Constraint, VT); } /// LowerAsmOperandForConstraint - Lower the specified operand into the Ops /// vector. If it is invalid, don't add anything to Ops. void MipsTargetLowering::LowerAsmOperandForConstraint(SDValue Op, std::string &Constraint, std::vector&Ops, SelectionDAG &DAG) const { SDLoc DL(Op); SDValue Result; // Only support length 1 constraints for now. if (Constraint.length() > 1) return; char ConstraintLetter = Constraint[0]; switch (ConstraintLetter) { default: break; // This will fall through to the generic implementation case 'I': // Signed 16 bit constant // If this fails, the parent routine will give an error if (ConstantSDNode *C = dyn_cast(Op)) { EVT Type = Op.getValueType(); int64_t Val = C->getSExtValue(); if (isInt<16>(Val)) { Result = DAG.getTargetConstant(Val, DL, Type); break; } } return; case 'J': // integer zero if (ConstantSDNode *C = dyn_cast(Op)) { EVT Type = Op.getValueType(); int64_t Val = C->getZExtValue(); if (Val == 0) { Result = DAG.getTargetConstant(0, DL, Type); break; } } return; case 'K': // unsigned 16 bit immediate if (ConstantSDNode *C = dyn_cast(Op)) { EVT Type = Op.getValueType(); uint64_t Val = (uint64_t)C->getZExtValue(); if (isUInt<16>(Val)) { Result = DAG.getTargetConstant(Val, DL, Type); break; } } return; case 'L': // signed 32 bit immediate where lower 16 bits are 0 if (ConstantSDNode *C = dyn_cast(Op)) { EVT Type = Op.getValueType(); int64_t Val = C->getSExtValue(); if ((isInt<32>(Val)) && ((Val & 0xffff) == 0)){ Result = DAG.getTargetConstant(Val, DL, Type); break; } } return; case 'N': // immediate in the range of -65535 to -1 (inclusive) if (ConstantSDNode *C = dyn_cast(Op)) { EVT Type = Op.getValueType(); int64_t Val = C->getSExtValue(); if ((Val >= -65535) && (Val <= -1)) { Result = DAG.getTargetConstant(Val, DL, Type); break; } } return; case 'O': // signed 15 bit immediate if (ConstantSDNode *C = dyn_cast(Op)) { EVT Type = Op.getValueType(); int64_t Val = C->getSExtValue(); if ((isInt<15>(Val))) { Result = DAG.getTargetConstant(Val, DL, Type); break; } } return; case 'P': // immediate in the range of 1 to 65535 (inclusive) if (ConstantSDNode *C = dyn_cast(Op)) { EVT Type = Op.getValueType(); int64_t Val = C->getSExtValue(); if ((Val <= 65535) && (Val >= 1)) { Result = DAG.getTargetConstant(Val, DL, Type); break; } } return; } if (Result.getNode()) { Ops.push_back(Result); return; } TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, Ops, DAG); } bool MipsTargetLowering::isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM, Type *Ty, unsigned AS, Instruction *I) const { // No global is ever allowed as a base. if (AM.BaseGV) return false; switch (AM.Scale) { case 0: // "r+i" or just "i", depending on HasBaseReg. break; case 1: if (!AM.HasBaseReg) // allow "r+i". break; return false; // disallow "r+r" or "r+r+i". default: return false; } return true; } bool MipsTargetLowering::isOffsetFoldingLegal(const GlobalAddressSDNode *GA) const { // The Mips target isn't yet aware of offsets. return false; } EVT MipsTargetLowering::getOptimalMemOpType(uint64_t Size, unsigned DstAlign, unsigned SrcAlign, bool IsMemset, bool ZeroMemset, bool MemcpyStrSrc, MachineFunction &MF) const { if (Subtarget.hasMips64()) return MVT::i64; return MVT::i32; } bool MipsTargetLowering::isFPImmLegal(const APFloat &Imm, EVT VT) const { if (VT != MVT::f32 && VT != MVT::f64) return false; if (Imm.isNegZero()) return false; return Imm.isZero(); } unsigned MipsTargetLowering::getJumpTableEncoding() const { // FIXME: For space reasons this should be: EK_GPRel32BlockAddress. if (ABI.IsN64() && isPositionIndependent()) return MachineJumpTableInfo::EK_GPRel64BlockAddress; return TargetLowering::getJumpTableEncoding(); } bool MipsTargetLowering::useSoftFloat() const { return Subtarget.useSoftFloat(); } void MipsTargetLowering::copyByValRegs( SDValue Chain, const SDLoc &DL, std::vector &OutChains, SelectionDAG &DAG, const ISD::ArgFlagsTy &Flags, SmallVectorImpl &InVals, const Argument *FuncArg, unsigned FirstReg, unsigned LastReg, const CCValAssign &VA, MipsCCState &State) const { MachineFunction &MF = DAG.getMachineFunction(); MachineFrameInfo &MFI = MF.getFrameInfo(); unsigned GPRSizeInBytes = Subtarget.getGPRSizeInBytes(); unsigned NumRegs = LastReg - FirstReg; unsigned RegAreaSize = NumRegs * GPRSizeInBytes; unsigned FrameObjSize = std::max(Flags.getByValSize(), RegAreaSize); int FrameObjOffset; ArrayRef ByValArgRegs = ABI.GetByValArgRegs(); if (RegAreaSize) FrameObjOffset = (int)ABI.GetCalleeAllocdArgSizeInBytes(State.getCallingConv()) - (int)((ByValArgRegs.size() - FirstReg) * GPRSizeInBytes); else FrameObjOffset = VA.getLocMemOffset(); // Create frame object. EVT PtrTy = getPointerTy(DAG.getDataLayout()); // Make the fixed object stored to mutable so that the load instructions // referencing it have their memory dependencies added. // Set the frame object as isAliased which clears the underlying objects // vector in ScheduleDAGInstrs::buildSchedGraph() resulting in addition of all // stores as dependencies for loads referencing this fixed object. int FI = MFI.CreateFixedObject(FrameObjSize, FrameObjOffset, false, true); SDValue FIN = DAG.getFrameIndex(FI, PtrTy); InVals.push_back(FIN); if (!NumRegs) return; // Copy arg registers. MVT RegTy = MVT::getIntegerVT(GPRSizeInBytes * 8); const TargetRegisterClass *RC = getRegClassFor(RegTy); for (unsigned I = 0; I < NumRegs; ++I) { unsigned ArgReg = ByValArgRegs[FirstReg + I]; unsigned VReg = addLiveIn(MF, ArgReg, RC); unsigned Offset = I * GPRSizeInBytes; SDValue StorePtr = DAG.getNode(ISD::ADD, DL, PtrTy, FIN, DAG.getConstant(Offset, DL, PtrTy)); SDValue Store = DAG.getStore(Chain, DL, DAG.getRegister(VReg, RegTy), StorePtr, MachinePointerInfo(FuncArg, Offset)); OutChains.push_back(Store); } } // Copy byVal arg to registers and stack. void MipsTargetLowering::passByValArg( SDValue Chain, const SDLoc &DL, std::deque> &RegsToPass, SmallVectorImpl &MemOpChains, SDValue StackPtr, MachineFrameInfo &MFI, SelectionDAG &DAG, SDValue Arg, unsigned FirstReg, unsigned LastReg, const ISD::ArgFlagsTy &Flags, bool isLittle, const CCValAssign &VA) const { unsigned ByValSizeInBytes = Flags.getByValSize(); unsigned OffsetInBytes = 0; // From beginning of struct unsigned RegSizeInBytes = Subtarget.getGPRSizeInBytes(); unsigned Alignment = std::min(Flags.getByValAlign(), RegSizeInBytes); EVT PtrTy = getPointerTy(DAG.getDataLayout()), RegTy = MVT::getIntegerVT(RegSizeInBytes * 8); unsigned NumRegs = LastReg - FirstReg; if (NumRegs) { ArrayRef ArgRegs = ABI.GetByValArgRegs(); bool LeftoverBytes = (NumRegs * RegSizeInBytes > ByValSizeInBytes); unsigned I = 0; // Copy words to registers. for (; I < NumRegs - LeftoverBytes; ++I, OffsetInBytes += RegSizeInBytes) { SDValue LoadPtr = DAG.getNode(ISD::ADD, DL, PtrTy, Arg, DAG.getConstant(OffsetInBytes, DL, PtrTy)); SDValue LoadVal = DAG.getLoad(RegTy, DL, Chain, LoadPtr, MachinePointerInfo(), Alignment); MemOpChains.push_back(LoadVal.getValue(1)); unsigned ArgReg = ArgRegs[FirstReg + I]; RegsToPass.push_back(std::make_pair(ArgReg, LoadVal)); } // Return if the struct has been fully copied. if (ByValSizeInBytes == OffsetInBytes) return; // Copy the remainder of the byval argument with sub-word loads and shifts. if (LeftoverBytes) { SDValue Val; for (unsigned LoadSizeInBytes = RegSizeInBytes / 2, TotalBytesLoaded = 0; OffsetInBytes < ByValSizeInBytes; LoadSizeInBytes /= 2) { unsigned RemainingSizeInBytes = ByValSizeInBytes - OffsetInBytes; if (RemainingSizeInBytes < LoadSizeInBytes) continue; // Load subword. SDValue LoadPtr = DAG.getNode(ISD::ADD, DL, PtrTy, Arg, DAG.getConstant(OffsetInBytes, DL, PtrTy)); SDValue LoadVal = DAG.getExtLoad( ISD::ZEXTLOAD, DL, RegTy, Chain, LoadPtr, MachinePointerInfo(), MVT::getIntegerVT(LoadSizeInBytes * 8), Alignment); MemOpChains.push_back(LoadVal.getValue(1)); // Shift the loaded value. unsigned Shamt; if (isLittle) Shamt = TotalBytesLoaded * 8; else Shamt = (RegSizeInBytes - (TotalBytesLoaded + LoadSizeInBytes)) * 8; SDValue Shift = DAG.getNode(ISD::SHL, DL, RegTy, LoadVal, DAG.getConstant(Shamt, DL, MVT::i32)); if (Val.getNode()) Val = DAG.getNode(ISD::OR, DL, RegTy, Val, Shift); else Val = Shift; OffsetInBytes += LoadSizeInBytes; TotalBytesLoaded += LoadSizeInBytes; Alignment = std::min(Alignment, LoadSizeInBytes); } unsigned ArgReg = ArgRegs[FirstReg + I]; RegsToPass.push_back(std::make_pair(ArgReg, Val)); return; } } // Copy remainder of byval arg to it with memcpy. unsigned MemCpySize = ByValSizeInBytes - OffsetInBytes; SDValue Src = DAG.getNode(ISD::ADD, DL, PtrTy, Arg, DAG.getConstant(OffsetInBytes, DL, PtrTy)); SDValue Dst = DAG.getNode(ISD::ADD, DL, PtrTy, StackPtr, DAG.getIntPtrConstant(VA.getLocMemOffset(), DL)); Chain = DAG.getMemcpy(Chain, DL, Dst, Src, DAG.getConstant(MemCpySize, DL, PtrTy), Alignment, /*isVolatile=*/false, /*AlwaysInline=*/false, /*isTailCall=*/false, MachinePointerInfo(), MachinePointerInfo()); MemOpChains.push_back(Chain); } void MipsTargetLowering::writeVarArgRegs(std::vector &OutChains, SDValue Chain, const SDLoc &DL, SelectionDAG &DAG, CCState &State) const { ArrayRef ArgRegs = ABI.GetVarArgRegs(); unsigned Idx = State.getFirstUnallocated(ArgRegs); unsigned RegSizeInBytes = Subtarget.getGPRSizeInBytes(); MVT RegTy = MVT::getIntegerVT(RegSizeInBytes * 8); const TargetRegisterClass *RC = getRegClassFor(RegTy); MachineFunction &MF = DAG.getMachineFunction(); MachineFrameInfo &MFI = MF.getFrameInfo(); MipsFunctionInfo *MipsFI = MF.getInfo(); // Offset of the first variable argument from stack pointer. int VaArgOffset; if (ArgRegs.size() == Idx) VaArgOffset = alignTo(State.getNextStackOffset(), RegSizeInBytes); else { VaArgOffset = (int)ABI.GetCalleeAllocdArgSizeInBytes(State.getCallingConv()) - (int)(RegSizeInBytes * (ArgRegs.size() - Idx)); } // Record the frame index of the first variable argument // which is a value necessary to VASTART. int FI = MFI.CreateFixedObject(RegSizeInBytes, VaArgOffset, true); MipsFI->setVarArgsFrameIndex(FI); // Copy the integer registers that have not been used for argument passing // to the argument register save area. For O32, the save area is allocated // in the caller's stack frame, while for N32/64, it is allocated in the // callee's stack frame. for (unsigned I = Idx; I < ArgRegs.size(); ++I, VaArgOffset += RegSizeInBytes) { unsigned Reg = addLiveIn(MF, ArgRegs[I], RC); SDValue ArgValue = DAG.getCopyFromReg(Chain, DL, Reg, RegTy); FI = MFI.CreateFixedObject(RegSizeInBytes, VaArgOffset, true); SDValue PtrOff = DAG.getFrameIndex(FI, getPointerTy(DAG.getDataLayout())); SDValue Store = DAG.getStore(Chain, DL, ArgValue, PtrOff, MachinePointerInfo()); cast(Store.getNode())->getMemOperand()->setValue( (Value *)nullptr); OutChains.push_back(Store); } } void MipsTargetLowering::HandleByVal(CCState *State, unsigned &Size, unsigned Align) const { const TargetFrameLowering *TFL = Subtarget.getFrameLowering(); assert(Size && "Byval argument's size shouldn't be 0."); Align = std::min(Align, TFL->getStackAlignment()); unsigned FirstReg = 0; unsigned NumRegs = 0; if (State->getCallingConv() != CallingConv::Fast) { unsigned RegSizeInBytes = Subtarget.getGPRSizeInBytes(); ArrayRef IntArgRegs = ABI.GetByValArgRegs(); // FIXME: The O32 case actually describes no shadow registers. const MCPhysReg *ShadowRegs = ABI.IsO32() ? IntArgRegs.data() : Mips64DPRegs; // We used to check the size as well but we can't do that anymore since // CCState::HandleByVal() rounds up the size after calling this function. assert(!(Align % RegSizeInBytes) && "Byval argument's alignment should be a multiple of" "RegSizeInBytes."); FirstReg = State->getFirstUnallocated(IntArgRegs); // If Align > RegSizeInBytes, the first arg register must be even. // FIXME: This condition happens to do the right thing but it's not the // right way to test it. We want to check that the stack frame offset // of the register is aligned. if ((Align > RegSizeInBytes) && (FirstReg % 2)) { State->AllocateReg(IntArgRegs[FirstReg], ShadowRegs[FirstReg]); ++FirstReg; } // Mark the registers allocated. Size = alignTo(Size, RegSizeInBytes); for (unsigned I = FirstReg; Size > 0 && (I < IntArgRegs.size()); Size -= RegSizeInBytes, ++I, ++NumRegs) State->AllocateReg(IntArgRegs[I], ShadowRegs[I]); } State->addInRegsParamInfo(FirstReg, FirstReg + NumRegs); } MachineBasicBlock *MipsTargetLowering::emitPseudoSELECT(MachineInstr &MI, MachineBasicBlock *BB, bool isFPCmp, unsigned Opc) const { assert(!(Subtarget.hasMips4() || Subtarget.hasMips32()) && "Subtarget already supports SELECT nodes with the use of" "conditional-move instructions."); const TargetInstrInfo *TII = Subtarget.getInstrInfo(); DebugLoc DL = MI.getDebugLoc(); // To "insert" a SELECT instruction, we actually have to insert the // diamond control-flow pattern. The incoming instruction knows the // destination vreg to set, the condition code register to branch on, the // true/false values to select between, and a branch opcode to use. const BasicBlock *LLVM_BB = BB->getBasicBlock(); MachineFunction::iterator It = ++BB->getIterator(); // thisMBB: // ... // TrueVal = ... // setcc r1, r2, r3 // bNE r1, r0, copy1MBB // fallthrough --> copy0MBB MachineBasicBlock *thisMBB = BB; MachineFunction *F = BB->getParent(); MachineBasicBlock *copy0MBB = F->CreateMachineBasicBlock(LLVM_BB); MachineBasicBlock *sinkMBB = F->CreateMachineBasicBlock(LLVM_BB); F->insert(It, copy0MBB); F->insert(It, sinkMBB); // Transfer the remainder of BB and its successor edges to sinkMBB. sinkMBB->splice(sinkMBB->begin(), BB, std::next(MachineBasicBlock::iterator(MI)), BB->end()); sinkMBB->transferSuccessorsAndUpdatePHIs(BB); // Next, add the true and fallthrough blocks as its successors. BB->addSuccessor(copy0MBB); BB->addSuccessor(sinkMBB); if (isFPCmp) { // bc1[tf] cc, sinkMBB BuildMI(BB, DL, TII->get(Opc)) .addReg(MI.getOperand(1).getReg()) .addMBB(sinkMBB); } else { // bne rs, $0, sinkMBB BuildMI(BB, DL, TII->get(Opc)) .addReg(MI.getOperand(1).getReg()) .addReg(Mips::ZERO) .addMBB(sinkMBB); } // copy0MBB: // %FalseValue = ... // # fallthrough to sinkMBB BB = copy0MBB; // Update machine-CFG edges BB->addSuccessor(sinkMBB); // sinkMBB: // %Result = phi [ %TrueValue, thisMBB ], [ %FalseValue, copy0MBB ] // ... BB = sinkMBB; BuildMI(*BB, BB->begin(), DL, TII->get(Mips::PHI), MI.getOperand(0).getReg()) .addReg(MI.getOperand(2).getReg()) .addMBB(thisMBB) .addReg(MI.getOperand(3).getReg()) .addMBB(copy0MBB); MI.eraseFromParent(); // The pseudo instruction is gone now. return BB; } // FIXME? Maybe this could be a TableGen attribute on some registers and // this table could be generated automatically from RegInfo. unsigned MipsTargetLowering::getRegisterByName(const char* RegName, EVT VT, SelectionDAG &DAG) const { // Named registers is expected to be fairly rare. For now, just support $28 // since the linux kernel uses it. if (Subtarget.isGP64bit()) { unsigned Reg = StringSwitch(RegName) .Case("$28", Mips::GP_64) .Default(0); if (Reg) return Reg; } else { unsigned Reg = StringSwitch(RegName) .Case("$28", Mips::GP) .Default(0); if (Reg) return Reg; } report_fatal_error("Invalid register name global variable"); } Index: llvm/trunk/lib/Target/Mips/MipsTargetMachine.cpp =================================================================== --- llvm/trunk/lib/Target/Mips/MipsTargetMachine.cpp (revision 336327) +++ llvm/trunk/lib/Target/Mips/MipsTargetMachine.cpp (revision 336328) @@ -1,329 +1,338 @@ //===-- MipsTargetMachine.cpp - Define TargetMachine for Mips -------------===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // Implements the info about Mips target spec. // //===----------------------------------------------------------------------===// #include "MipsTargetMachine.h" #include "MCTargetDesc/MipsABIInfo.h" #include "MCTargetDesc/MipsMCTargetDesc.h" #include "Mips.h" #include "Mips16ISelDAGToDAG.h" #include "MipsSEISelDAGToDAG.h" #include "MipsSubtarget.h" #include "MipsTargetObjectFile.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/CodeGen/GlobalISel/IRTranslator.h" #include "llvm/CodeGen/GlobalISel/Legalizer.h" #include "llvm/CodeGen/GlobalISel/RegBankSelect.h" #include "llvm/CodeGen/GlobalISel/InstructionSelect.h" #include "llvm/CodeGen/BasicTTIImpl.h" #include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/TargetPassConfig.h" #include "llvm/IR/Attributes.h" #include "llvm/IR/Function.h" #include "llvm/Support/CodeGen.h" #include "llvm/Support/Debug.h" #include "llvm/Support/TargetRegistry.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Target/TargetOptions.h" #include using namespace llvm; #define DEBUG_TYPE "mips" extern "C" void LLVMInitializeMipsTarget() { // Register the target. RegisterTargetMachine X(getTheMipsTarget()); RegisterTargetMachine Y(getTheMipselTarget()); RegisterTargetMachine A(getTheMips64Target()); RegisterTargetMachine B(getTheMips64elTarget()); PassRegistry *PR = PassRegistry::getPassRegistry(); initializeGlobalISel(*PR); initializeMipsDelaySlotFillerPass(*PR); initializeMipsBranchExpansionPass(*PR); initializeMicroMipsSizeReducePass(*PR); } static std::string computeDataLayout(const Triple &TT, StringRef CPU, const TargetOptions &Options, bool isLittle) { std::string Ret; MipsABIInfo ABI = MipsABIInfo::computeTargetABI(TT, CPU, Options.MCOptions); // There are both little and big endian mips. if (isLittle) Ret += "e"; else Ret += "E"; if (ABI.IsO32()) Ret += "-m:m"; else Ret += "-m:e"; // Pointers are 32 bit on some ABIs. if (!ABI.IsN64()) Ret += "-p:32:32"; // 8 and 16 bit integers only need to have natural alignment, but try to // align them to 32 bits. 64 bit integers have natural alignment. Ret += "-i8:8:32-i16:16:32-i64:64"; // 32 bit registers are always available and the stack is at least 64 bit // aligned. On N64 64 bit registers are also available and the stack is // 128 bit aligned. if (ABI.IsN64() || ABI.IsN32()) Ret += "-n32:64-S128"; else Ret += "-n32-S64"; return Ret; } static Reloc::Model getEffectiveRelocModel(bool JIT, Optional RM) { if (!RM.hasValue() || JIT) return Reloc::Static; return *RM; } static CodeModel::Model getEffectiveCodeModel(Optional CM) { if (CM) return *CM; return CodeModel::Small; } // On function prologue, the stack is created by decrementing // its pointer. Once decremented, all references are done with positive // offset from the stack/frame pointer, using StackGrowsUp enables // an easier handling. // Using CodeModel::Large enables different CALL behavior. MipsTargetMachine::MipsTargetMachine(const Target &T, const Triple &TT, StringRef CPU, StringRef FS, const TargetOptions &Options, Optional RM, Optional CM, CodeGenOpt::Level OL, bool JIT, bool isLittle) : LLVMTargetMachine(T, computeDataLayout(TT, CPU, Options, isLittle), TT, CPU, FS, Options, getEffectiveRelocModel(JIT, RM), getEffectiveCodeModel(CM), OL), isLittle(isLittle), TLOF(llvm::make_unique()), ABI(MipsABIInfo::computeTargetABI(TT, CPU, Options.MCOptions)), Subtarget(nullptr), DefaultSubtarget(TT, CPU, FS, isLittle, *this, Options.StackAlignmentOverride), NoMips16Subtarget(TT, CPU, FS.empty() ? "-mips16" : FS.str() + ",-mips16", isLittle, *this, Options.StackAlignmentOverride), Mips16Subtarget(TT, CPU, FS.empty() ? "+mips16" : FS.str() + ",+mips16", isLittle, *this, Options.StackAlignmentOverride) { Subtarget = &DefaultSubtarget; initAsmInfo(); } MipsTargetMachine::~MipsTargetMachine() = default; void MipsebTargetMachine::anchor() {} MipsebTargetMachine::MipsebTargetMachine(const Target &T, const Triple &TT, StringRef CPU, StringRef FS, const TargetOptions &Options, Optional RM, Optional CM, CodeGenOpt::Level OL, bool JIT) : MipsTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, JIT, false) {} void MipselTargetMachine::anchor() {} MipselTargetMachine::MipselTargetMachine(const Target &T, const Triple &TT, StringRef CPU, StringRef FS, const TargetOptions &Options, Optional RM, Optional CM, CodeGenOpt::Level OL, bool JIT) : MipsTargetMachine(T, TT, CPU, FS, Options, RM, CM, OL, JIT, true) {} const MipsSubtarget * MipsTargetMachine::getSubtargetImpl(const Function &F) const { Attribute CPUAttr = F.getFnAttribute("target-cpu"); Attribute FSAttr = F.getFnAttribute("target-features"); std::string CPU = !CPUAttr.hasAttribute(Attribute::None) ? CPUAttr.getValueAsString().str() : TargetCPU; std::string FS = !FSAttr.hasAttribute(Attribute::None) ? FSAttr.getValueAsString().str() : TargetFS; bool hasMips16Attr = !F.getFnAttribute("mips16").hasAttribute(Attribute::None); bool hasNoMips16Attr = !F.getFnAttribute("nomips16").hasAttribute(Attribute::None); bool HasMicroMipsAttr = !F.getFnAttribute("micromips").hasAttribute(Attribute::None); bool HasNoMicroMipsAttr = !F.getFnAttribute("nomicromips").hasAttribute(Attribute::None); // FIXME: This is related to the code below to reset the target options, // we need to know whether or not the soft float flag is set on the // function, so we can enable it as a subtarget feature. bool softFloat = F.hasFnAttribute("use-soft-float") && F.getFnAttribute("use-soft-float").getValueAsString() == "true"; if (hasMips16Attr) FS += FS.empty() ? "+mips16" : ",+mips16"; else if (hasNoMips16Attr) FS += FS.empty() ? "-mips16" : ",-mips16"; if (HasMicroMipsAttr) FS += FS.empty() ? "+micromips" : ",+micromips"; else if (HasNoMicroMipsAttr) FS += FS.empty() ? "-micromips" : ",-micromips"; if (softFloat) FS += FS.empty() ? "+soft-float" : ",+soft-float"; auto &I = SubtargetMap[CPU + FS]; if (!I) { // This needs to be done before we create a new subtarget since any // creation will depend on the TM and the code generation flags on the // function that reside in TargetOptions. resetTargetOptions(F); I = llvm::make_unique(TargetTriple, CPU, FS, isLittle, *this, Options.StackAlignmentOverride); } return I.get(); } void MipsTargetMachine::resetSubtarget(MachineFunction *MF) { LLVM_DEBUG(dbgs() << "resetSubtarget\n"); Subtarget = const_cast(getSubtargetImpl(MF->getFunction())); MF->setSubtarget(Subtarget); } namespace { /// Mips Code Generator Pass Configuration Options. class MipsPassConfig : public TargetPassConfig { public: MipsPassConfig(MipsTargetMachine &TM, PassManagerBase &PM) : TargetPassConfig(TM, PM) { // The current implementation of long branch pass requires a scratch // register ($at) to be available before branch instructions. Tail merging // can break this requirement, so disable it when long branch pass is // enabled. EnableTailMerge = !getMipsSubtarget().enableLongBranchPass(); } MipsTargetMachine &getMipsTargetMachine() const { return getTM(); } const MipsSubtarget &getMipsSubtarget() const { return *getMipsTargetMachine().getSubtargetImpl(); } void addIRPasses() override; bool addInstSelector() override; void addPreEmitPass() override; void addPreRegAlloc() override; + void addPreEmit2() ; bool addIRTranslator() override; bool addLegalizeMachineIR() override; bool addRegBankSelect() override; bool addGlobalInstructionSelect() override; }; } // end anonymous namespace TargetPassConfig *MipsTargetMachine::createPassConfig(PassManagerBase &PM) { return new MipsPassConfig(*this, PM); } void MipsPassConfig::addIRPasses() { TargetPassConfig::addIRPasses(); addPass(createAtomicExpandPass()); if (getMipsSubtarget().os16()) addPass(createMipsOs16Pass()); if (getMipsSubtarget().inMips16HardFloat()) addPass(createMips16HardFloatPass()); } // Install an instruction selector pass using // the ISelDag to gen Mips code. bool MipsPassConfig::addInstSelector() { addPass(createMipsModuleISelDagPass()); addPass(createMips16ISelDag(getMipsTargetMachine(), getOptLevel())); addPass(createMipsSEISelDag(getMipsTargetMachine(), getOptLevel())); return false; } void MipsPassConfig::addPreRegAlloc() { addPass(createMipsOptimizePICCallPass()); } TargetTransformInfo MipsTargetMachine::getTargetTransformInfo(const Function &F) { if (Subtarget->allowMixed16_32()) { LLVM_DEBUG(errs() << "No Target Transform Info Pass Added\n"); // FIXME: This is no longer necessary as the TTI returned is per-function. return TargetTransformInfo(F.getParent()->getDataLayout()); } LLVM_DEBUG(errs() << "Target Transform Info Pass Added\n"); return TargetTransformInfo(BasicTTIImpl(this, F)); } +void MipsPassConfig::addPreEmit2() { +} + // Implemented by targets that want to run passes immediately before // machine code is emitted. return true if -print-machineinstrs should // print out the code after the passes. void MipsPassConfig::addPreEmitPass() { + // Expand pseudo instructions that are sensitive to register allocation. + addPass(createMipsExpandPseudoPass()); + + // The microMIPS size reduction pass performs instruction reselection for + // instructions which can be remapped to a 16 bit instruction. addPass(createMicroMipsSizeReducePass()); // The delay slot filler pass can potientially create forbidden slot hazards // for MIPSR6 and therefore it should go before MipsBranchExpansion pass. addPass(createMipsDelaySlotFillerPass()); // This pass expands branches and takes care about the forbidden slot hazards. // Expanding branches may potentially create forbidden slot hazards for // MIPSR6, and fixing such hazard may potentially break a branch by extending // its offset out of range. That's why this pass combine these two tasks, and // runs them alternately until one of them finishes without any changes. Only // then we can be sure that all branches are expanded properly and no hazards // exists. // Any new pass should go before this pass. addPass(createMipsBranchExpansion()); addPass(createMipsConstantIslandPass()); } bool MipsPassConfig::addIRTranslator() { addPass(new IRTranslator()); return false; } bool MipsPassConfig::addLegalizeMachineIR() { addPass(new Legalizer()); return false; } bool MipsPassConfig::addRegBankSelect() { addPass(new RegBankSelect()); return false; } bool MipsPassConfig::addGlobalInstructionSelect() { addPass(new InstructionSelect()); return false; }