From: Artyom Skrobov Date: Wed, 28 Oct 2015 13:58:36 +0000 (+0000) Subject: [ARM] Allow SP in rGPR, starting from ARMv8 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=d024add676337be2a2bc79982fc374e2eafd0fc1;p=oota-llvm.git [ARM] Allow SP in rGPR, starting from ARMv8 Summary: This patch handles assembly and disassembly, but not codegen, as of yet. Additionally, it fixes a bug whereby SP and PC as shifted-reg operands were treated as predictable in ARMv7 Thumb; and it enables the tests for invalid and unpredictable instructions to run on both ARMv7 and ARMv8. Reviewers: jmolloy, rengolin Subscribers: aemerson, rengolin, llvm-commits Differential Revision: http://reviews.llvm.org/D14141 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@251516 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index b0bde037b91..5a82cf36bda 100644 --- a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -343,6 +343,7 @@ public: Match_RequiresNotITBlock, Match_RequiresV6, Match_RequiresThumb2, + Match_RequiresV8, #define GET_OPERAND_DIAGNOSTIC_TYPES #include "ARMGenAsmMatcher.inc" @@ -8529,18 +8530,29 @@ unsigned ARMAsmParser::checkTargetMatchPredicate(MCInst &Inst) { if (isThumbTwo() && Inst.getOperand(OpNo).getReg() == ARM::CPSR && inITBlock()) return Match_RequiresNotITBlock; + } else if (isThumbOne()) { + // Some high-register supporting Thumb1 encodings only allow both registers + // to be from r0-r7 when in Thumb2. + if (Opc == ARM::tADDhirr && !hasV6MOps() && + isARMLowRegister(Inst.getOperand(1).getReg()) && + isARMLowRegister(Inst.getOperand(2).getReg())) + return Match_RequiresThumb2; + // Others only require ARMv6 or later. + else if (Opc == ARM::tMOVr && !hasV6Ops() && + isARMLowRegister(Inst.getOperand(0).getReg()) && + isARMLowRegister(Inst.getOperand(1).getReg())) + return Match_RequiresV6; } - // Some high-register supporting Thumb1 encodings only allow both registers - // to be from r0-r7 when in Thumb2. - else if (Opc == ARM::tADDhirr && isThumbOne() && !hasV6MOps() && - isARMLowRegister(Inst.getOperand(1).getReg()) && - isARMLowRegister(Inst.getOperand(2).getReg())) - return Match_RequiresThumb2; - // Others only require ARMv6 or later. - else if (Opc == ARM::tMOVr && isThumbOne() && !hasV6Ops() && - isARMLowRegister(Inst.getOperand(0).getReg()) && - isARMLowRegister(Inst.getOperand(1).getReg())) - return Match_RequiresV6; + + for (unsigned I = 0; I < MCID.NumOperands; ++I) + if (MCID.OpInfo[I].RegClass == ARM::rGPRRegClassID) { + // rGPRRegClass excludes PC, and also excluded SP before ARMv8 + if ((Inst.getOperand(I).getReg() == ARM::SP) && !hasV8Ops()) + return Match_RequiresV8; + else if (Inst.getOperand(I).getReg() == ARM::PC) + return Match_InvalidOperand; + } + return Match_Success; } @@ -8639,6 +8651,8 @@ bool ARMAsmParser::MatchAndEmitInstruction(SMLoc IDLoc, unsigned &Opcode, return Error(IDLoc, "instruction variant requires ARMv6 or later"); case Match_RequiresThumb2: return Error(IDLoc, "instruction variant requires Thumb2"); + case Match_RequiresV8: + return Error(IDLoc, "instruction variant requires ARMv8 or later"); case Match_ImmRange0_15: { SMLoc ErrorLoc = ((ARMOperand &)*Operands[ErrorInfo]).getStartLoc(); if (ErrorLoc == SMLoc()) ErrorLoc = IDLoc; @@ -9996,6 +10010,10 @@ unsigned ARMAsmParser::validateTargetOperandClass(MCParsedAsmOperand &AsmOp, "expression value must be representable in 32 bits"); } break; + case MCK_rGPR: + if (hasV8Ops() && Op.isReg() && Op.getReg() == ARM::SP) + return Match_Success; + break; case MCK_GPRPair: if (Op.isReg() && MRI->getRegClass(ARM::GPRRegClassID).contains(Op.getReg())) diff --git a/lib/Target/ARM/Disassembler/ARMDisassembler.cpp b/lib/Target/ARM/Disassembler/ARMDisassembler.cpp index 38cb585edc0..04e4245b633 100644 --- a/lib/Target/ARM/Disassembler/ARMDisassembler.cpp +++ b/lib/Target/ARM/Disassembler/ARMDisassembler.cpp @@ -966,8 +966,13 @@ static DecodeStatus DecodetcGPRRegisterClass(MCInst &Inst, unsigned RegNo, static DecodeStatus DecoderGPRRegisterClass(MCInst &Inst, unsigned RegNo, uint64_t Address, const void *Decoder) { DecodeStatus S = MCDisassembler::Success; - if (RegNo == 13 || RegNo == 15) + + const FeatureBitset &featureBits = + ((const MCDisassembler*)Decoder)->getSubtargetInfo().getFeatureBits(); + + if ((RegNo == 13 && !featureBits[ARM::HasV8Ops]) || RegNo == 15) S = MCDisassembler::SoftFail; + Check(S, DecodeGPRRegisterClass(Inst, RegNo, Address, Decoder)); return S; } @@ -1127,7 +1132,7 @@ static DecodeStatus DecodeSORegImmOperand(MCInst &Inst, unsigned Val, unsigned imm = fieldFromInstruction(Val, 7, 5); // Register-immediate - if (!Check(S, DecodeGPRRegisterClass(Inst, Rm, Address, Decoder))) + if (!Check(S, DecoderGPRRegisterClass(Inst, Rm, Address, Decoder))) return MCDisassembler::Fail; ARM_AM::ShiftOpc Shift = ARM_AM::lsl; diff --git a/test/MC/ARM/basic-thumb2-instructions-v8.s b/test/MC/ARM/basic-thumb2-instructions-v8.s index a7882aead01..46bc1b91ffa 100644 --- a/test/MC/ARM/basic-thumb2-instructions-v8.s +++ b/test/MC/ARM/basic-thumb2-instructions-v8.s @@ -3,7 +3,7 @@ @ RUN: llvm-mc -triple thumbv8 -show-encoding < %s | FileCheck %s --check-prefix=CHECK-V8 @ RUN: not llvm-mc -triple thumbv7 -show-encoding < %s 2>&1 | FileCheck %s --check-prefix=CHECK-V7 -@ HLT +@ HLT (in ARMv8 only) hlt #0 hlt #63 @ CHECK-V8: hlt #0 @ encoding: [0x80,0xba] @@ -19,12 +19,23 @@ @ CHECK-V8: hlt #24 @ encoding: [0x98,0xba] @ CHECK-V7: error: instruction requires: armv8 -@ Can accept AL condition code +@ Can accept AL condition code (in ARMv8 only) hltal #24 @ CHECK-V8: hlt #24 @ encoding: [0x98,0xba] @ CHECK-V7: error: instruction requires: armv8 -@ DCPS{1,2,3} +@ Can accept SP as rGPR (in ARMv8 only) + sbc.w r6, r3, sp, asr #16 + and.w r6, r3, sp, asr #16 + and sp, r0, #0 +@ CHECK-V8: sbc.w r6, r3, sp, asr #16 @ encoding: [0x63,0xeb,0x2d,0x46] +@ CHECK-V8: and.w r6, r3, sp, asr #16 @ encoding: [0x03,0xea,0x2d,0x46] +@ CHECK-V8: and sp, r0, #0 @ encoding: [0x00,0xf0,0x00,0x0d] +@ CHECK-V7: error: instruction variant requires ARMv8 or later +@ CHECK-V7: error: instruction variant requires ARMv8 or later +@ CHECK-V7: error: invalid operand for instruction + +@ DCPS{1,2,3} (in ARMv8 only) dcps1 dcps2 dcps3 @@ -36,7 +47,7 @@ @ CHECK-V7: error: instruction requires: armv8 @------------------------------------------------------------------------------ -@ DMB (v8 barriers) +@ DMB (ARMv8-only barriers) @------------------------------------------------------------------------------ dmb ishld dmb oshld @@ -53,7 +64,7 @@ @ CHECK-V7: error: invalid operand for instruction @------------------------------------------------------------------------------ -@ DSB (v8 barriers) +@ DSB (ARMv8-only barriers) @------------------------------------------------------------------------------ dsb ishld dsb oshld @@ -70,7 +81,7 @@ @ CHECK-V7: error: invalid operand for instruction @------------------------------------------------------------------------------ -@ SEVL +@ SEVL (in ARMv8 only) @------------------------------------------------------------------------------ sevl sevl.w diff --git a/test/MC/ARM/diagnostics.s b/test/MC/ARM/diagnostics.s index 6f66dc3b4d0..a1dd95f7d7f 100644 --- a/test/MC/ARM/diagnostics.s +++ b/test/MC/ARM/diagnostics.s @@ -1,7 +1,7 @@ @ RUN: not llvm-mc -triple=armv7-apple-darwin < %s 2> %t -@ RUN: FileCheck --check-prefix=CHECK-ERRORS < %t %s +@ RUN: FileCheck --check-prefix=CHECK-ERRORS --check-prefix=CHECK-ERRORS-V7 < %t %s @ RUN: not llvm-mc -triple=armv8 < %s 2> %t -@ RUN: FileCheck --check-prefix=CHECK-ERRORS-V8 < %t %s +@ RUN: FileCheck --check-prefix=CHECK-ERRORS --check-prefix=CHECK-ERRORS-V8 < %t %s @ Check for various assembly diagnostic messages on invalid input. @@ -98,22 +98,22 @@ @ Out of range immediates for v8 HLT instruction. hlt #65536 hlt #-1 -@CHECK-ERRORS-V8: error: invalid operand for instruction -@CHECK-ERRORS-V8: hlt #65536 -@CHECK-ERRORS-V8: ^ -@CHECK-ERRORS-V8: error: invalid operand for instruction -@CHECK-ERRORS-V8: hlt #-1 -@CHECK-ERRORS-V8: ^ +@CHECK-ERRORS: error: invalid operand for instruction +@CHECK-ERRORS: hlt #65536 +@CHECK-ERRORS: ^ +@CHECK-ERRORS: error: invalid operand for instruction +@CHECK-ERRORS: hlt #-1 +@CHECK-ERRORS: ^ @ Illegal condition code for v8 HLT instruction. hlteq #2 hltlt #23 -@CHECK-ERRORS-V8: error: instruction 'hlt' is not predicable, but condition code specified -@CHECK-ERRORS-V8: hlteq #2 -@CHECK-ERRORS-V8: ^ -@CHECK-ERRORS-V8: error: instruction 'hlt' is not predicable, but condition code specified -@CHECK-ERRORS-V8: hltlt #23 -@CHECK-ERRORS-V8: ^ +@CHECK-ERRORS: error: instruction 'hlt' is not predicable, but condition code specified +@CHECK-ERRORS: hlteq #2 +@CHECK-ERRORS: ^ +@CHECK-ERRORS: error: instruction 'hlt' is not predicable, but condition code specified +@CHECK-ERRORS: hltlt #23 +@CHECK-ERRORS: ^ @ Out of range 4 and 3 bit immediates on CDP[2] @@ -149,7 +149,8 @@ @ CHECK-ERRORS: error: invalid operand for instruction @ CHECK-ERRORS: error: invalid operand for instruction @ CHECK-ERRORS: error: immediate operand must be in the range [0,15] -@ CHECK-ERRORS: error: immediate operand must be in the range [0,15] +@ CHECK-ERRORS-V7: error: immediate operand must be in the range [0,15] +@ CHECK-ERRORS-V8: error: invalid operand for instruction @ p10 and p11 are reserved for NEON mcr p10, #2, r5, c1, c1, #4 @@ -183,7 +184,8 @@ @ CHECK-ERRORS: error: invalid operand for instruction @ CHECK-ERRORS: error: invalid operand for instruction @ CHECK-ERRORS: error: immediate operand must be in the range [0,15] -@ CHECK-ERRORS: error: immediate operand must be in the range [0,15] +@ CHECK-ERRORS-V7: error: immediate operand must be in the range [0,15] +@ CHECK-ERRORS-V8: error: invalid operand for instruction @ Shifter operand validation for PKH instructions. pkhbt r2, r2, r3, lsl #-1 @@ -394,12 +396,14 @@ ldc2 p2, c8, [r1], { 256 } ldc2 p2, c8, [r1], { -1 } -@ CHECK-ERRORS: error: coprocessor option must be an immediate in range [0, 255] -@ CHECK-ERRORS: ldc2 p2, c8, [r1], { 256 } -@ CHECK-ERRORS: ^ -@ CHECK-ERRORS: error: coprocessor option must be an immediate in range [0, 255] -@ CHECK-ERRORS: ldc2 p2, c8, [r1], { -1 } -@ CHECK-ERRORS: ^ +@ CHECK-ERRORS-V7: error: coprocessor option must be an immediate in range [0, 255] +@ CHECK-ERRORS-V7: ldc2 p2, c8, [r1], { 256 } +@ CHECK-ERRORS-V7: ^ +@ CHECK-ERRORS-V8: error: register expected +@ CHECK-ERRORS-V7: error: coprocessor option must be an immediate in range [0, 255] +@ CHECK-ERRORS-V7: ldc2 p2, c8, [r1], { -1 } +@ CHECK-ERRORS-V7: ^ +@ CHECK-ERRORS-V8: error: register expected @ Bad CPS instruction format. cps f,#1 @@ -470,14 +474,14 @@ vrintn.f32 s8, s9 vrintp.f64.f64 d10, d11 vrintm.f64 d12, d13 -@ CHECK-ERRORS: error: instruction requires: FPARMv8 -@ CHECK-ERRORS: error: instruction requires: FPARMv8 -@ CHECK-ERRORS: error: instruction requires: FPARMv8 -@ CHECK-ERRORS: error: instruction requires: FPARMv8 -@ CHECK-ERRORS: error: instruction requires: FPARMv8 -@ CHECK-ERRORS: error: instruction requires: FPARMv8 -@ CHECK-ERRORS: error: instruction requires: FPARMv8 -@ CHECK-ERRORS: error: instruction requires: FPARMv8 +@ CHECK-ERRORS-V7: error: instruction requires: FPARMv8 +@ CHECK-ERRORS-V7: error: instruction requires: FPARMv8 +@ CHECK-ERRORS-V7: error: instruction requires: FPARMv8 +@ CHECK-ERRORS-V7: error: instruction requires: FPARMv8 +@ CHECK-ERRORS-V7: error: instruction requires: FPARMv8 +@ CHECK-ERRORS-V7: error: instruction requires: FPARMv8 +@ CHECK-ERRORS-V7: error: instruction requires: FPARMv8 +@ CHECK-ERRORS-V7: error: instruction requires: FPARMv8 stm sp!, {r0, pc}^ ldm sp!, {r0}^ diff --git a/test/MC/ARM/thumb-shift-encoding.s b/test/MC/ARM/thumb-shift-encoding.s index 54284132b65..ad35aff4505 100644 --- a/test/MC/ARM/thumb-shift-encoding.s +++ b/test/MC/ARM/thumb-shift-encoding.s @@ -6,40 +6,40 @@ sbc.w r12, lr, r0 sbc.w r1, r8, r9, lsr #32 - sbc.w r2, r7, pc, lsr #16 + sbc.w r2, r7, r10, lsr #16 sbc.w r3, r6, r10, lsl #0 sbc.w r4, r5, lr, lsl #16 sbc.w r5, r4, r11, asr #32 - sbc.w r6, r3, sp, asr #16 + sbc.w r6, r3, r12, asr #16 sbc.w r7, r2, r12, rrx sbc.w r8, r1, r0, ror #16 @ CHECK: sbc.w r12, lr, r0 @ encoding: [0x6e,0xeb,0x00,0x0c] @ CHECK: sbc.w r1, r8, r9, lsr #32 @ encoding: [0x68,0xeb,0x19,0x01] -@ CHECK: sbc.w r2, r7, pc, lsr #16 @ encoding: [0x67,0xeb,0x1f,0x42] +@ CHECK: sbc.w r2, r7, r10, lsr #16 @ encoding: [0x67,0xeb,0x1a,0x42] @ CHECK: sbc.w r3, r6, r10 @ encoding: [0x66,0xeb,0x0a,0x03] @ CHECK: sbc.w r4, r5, lr, lsl #16 @ encoding: [0x65,0xeb,0x0e,0x44] @ CHECK: sbc.w r5, r4, r11, asr #32 @ encoding: [0x64,0xeb,0x2b,0x05] -@ CHECK: sbc.w r6, r3, sp, asr #16 @ encoding: [0x63,0xeb,0x2d,0x46] +@ CHECK: sbc.w r6, r3, r12, asr #16 @ encoding: [0x63,0xeb,0x2c,0x46] @ CHECK: sbc.w r7, r2, r12, rrx @ encoding: [0x62,0xeb,0x3c,0x07] @ CHECK: sbc.w r8, r1, r0, ror #16 @ encoding: [0x61,0xeb,0x30,0x48] and.w r12, lr, r0 and.w r1, r8, r9, lsr #32 - and.w r2, r7, pc, lsr #16 + and.w r2, r7, r10, lsr #16 and.w r3, r6, r10, lsl #0 and.w r4, r5, lr, lsl #16 and.w r5, r4, r11, asr #32 - and.w r6, r3, sp, asr #16 + and.w r6, r3, r12, asr #16 and.w r7, r2, r12, rrx and.w r8, r1, r0, ror #16 @ CHECK: and.w r12, lr, r0 @ encoding: [0x0e,0xea,0x00,0x0c] @ CHECK: and.w r1, r8, r9, lsr #32 @ encoding: [0x08,0xea,0x19,0x01] -@ CHECK: and.w r2, r7, pc, lsr #16 @ encoding: [0x07,0xea,0x1f,0x42] +@ CHECK: and.w r2, r7, r10, lsr #16 @ encoding: [0x07,0xea,0x1a,0x42] @ CHECK: and.w r3, r6, r10 @ encoding: [0x06,0xea,0x0a,0x03] @ CHECK: and.w r4, r5, lr, lsl #16 @ encoding: [0x05,0xea,0x0e,0x44] @ CHECK: and.w r5, r4, r11, asr #32 @ encoding: [0x04,0xea,0x2b,0x05] -@ CHECK: and.w r6, r3, sp, asr #16 @ encoding: [0x03,0xea,0x2d,0x46] +@ CHECK: and.w r6, r3, r12, asr #16 @ encoding: [0x03,0xea,0x2c,0x46] @ CHECK: and.w r7, r2, r12, rrx @ encoding: [0x02,0xea,0x3c,0x07] @ CHECK: and.w r8, r1, r0, ror #16 @ encoding: [0x01,0xea,0x30,0x48] diff --git a/test/MC/ARM/thumb2-diagnostics.s b/test/MC/ARM/thumb2-diagnostics.s index 8fd161c2cc5..96978899faa 100644 --- a/test/MC/ARM/thumb2-diagnostics.s +++ b/test/MC/ARM/thumb2-diagnostics.s @@ -1,5 +1,8 @@ @ RUN: not llvm-mc -triple=thumbv7-apple-darwin < %s 2> %t -@ RUN: FileCheck --check-prefix=CHECK-ERRORS < %t %s +@ RUN: FileCheck --check-prefix=CHECK-ERRORS --check-prefix=CHECK-ERRORS-V7 < %t %s + +@ RUN: not llvm-mc -triple=thumbv8-apple-darwin < %s 2> %t +@ RUN: FileCheck --check-prefix=CHECK-ERRORS --check-prefix=CHECK-ERRORS-V8 < %t %s @ Ill-formed IT block instructions. itet eq @@ -41,7 +44,8 @@ @ CHECK-ERRORS: error: invalid operand for instruction @ CHECK-ERRORS: error: invalid operand for instruction @ CHECK-ERRORS: error: immediate operand must be in the range [0,15] -@ CHECK-ERRORS: error: immediate operand must be in the range [0,15] +@ CHECK-ERRORS-V7: error: immediate operand must be in the range [0,15] +@ CHECK-ERRORS-V8: error: invalid operand for instruction isb #-1 isb #16 @@ -87,7 +91,14 @@ foo2: @ CHECK-ERRORS: error: invalid operand for instruction @ CHECK-ERRORS: error: invalid operand for instruction -ssat r0, #1, r0, asr #32 -usat r0, #1, r0, asr #32 + ssat r0, #1, r0, asr #32 + usat r0, #1, r0, asr #32 @ CHECK-ERRORS: error: 'asr #32' shift amount not allowed in Thumb mode @ CHECK-ERRORS: error: 'asr #32' shift amount not allowed in Thumb mode + + @ PC is not valid as shifted-rGPR + sbc.w r2, r7, pc, lsr #16 + and.w r2, r7, pc, lsr #16 +@ CHECK-ERRORS: error: invalid operand for instruction +@ CHECK-ERRORS: error: invalid operand for instruction + diff --git a/test/MC/Disassembler/ARM/invalid-thumbv7.txt b/test/MC/Disassembler/ARM/invalid-thumbv7.txt index 5257633e579..512fc5d237e 100644 --- a/test/MC/Disassembler/ARM/invalid-thumbv7.txt +++ b/test/MC/Disassembler/ARM/invalid-thumbv7.txt @@ -1,4 +1,5 @@ -# RUN: not llvm-mc -disassemble %s -mcpu cortex-a8 -triple thumbv7 2>&1 | FileCheck %s +# RUN: not llvm-mc -disassemble %s -mcpu cortex-a8 -triple thumbv7 2>&1 | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-V7 +# RUN: not llvm-mc -disassemble %s -mcpu cortex-a53 -triple thumbv8 2>&1 | FileCheck %s # This file is checking Thumbv7 encodings which are globally invalid, usually due # to the constraints of the instructions not being met. For example invalid @@ -359,10 +360,22 @@ # 32-bit Thumb STM instructions cannot have a writeback register which appears # in the list. -[0xa1,0xe8,0x07,0x04] +[0xa1 0xe8 0x07 0x04] # CHECK: warning: potentially undefined instruction encoding -# CHECK-NEXT: [0xa1,0xe8,0x07,0x04] +# CHECK-NEXT: [0xa1 0xe8 0x07 0x04] -[0x21,0xe9,0x07,0x04] +[0x21 0xe9 0x07 0x04] # CHECK: warning: potentially undefined instruction encoding -# CHECK-NEXT: [0x21,0xe9,0x07,0x04] +# CHECK-NEXT: [0x21 0xe9 0x07 0x04] + +#------------------------------------------------------------------------------ +# SP is invalid as rGPR before ARMv8 +#------------------------------------------------------------------------------ + +[0x00 0xf0 0x00 0x0d] +# CHECK-V7: warning: potentially undefined instruction encoding +# CHECK-V7-NEXT: [0x00 0xf0 0x00 0x0d] + +[0x63 0xeb 0x2d 0x46] +# CHECK-V7: warning: potentially undefined instruction encoding +# CHECK-V7-NEXT: [0x63 0xeb 0x2d 0x46] diff --git a/test/MC/Disassembler/ARM/thumb-v8.txt b/test/MC/Disassembler/ARM/thumb-v8.txt index eb5ffea7d66..1bcf654a479 100644 --- a/test/MC/Disassembler/ARM/thumb-v8.txt +++ b/test/MC/Disassembler/ARM/thumb-v8.txt @@ -1,4 +1,5 @@ -# RUN: llvm-mc -disassemble -triple thumbv8 -mattr=+db -show-encoding < %s | FileCheck %s +# RUN: llvm-mc -disassemble -triple thumbv8 -mattr=+db -show-encoding 2>%t < %s | FileCheck %s +# RUN: FileCheck -allow-empty -check-prefix=STDERR < %t %s 0x80 0xba # CHECK: hlt #0 @@ -26,3 +27,10 @@ # CHECK: dmb oshld # CHECK: dmb nshld # CHECK: dmb ld + +[0x00 0xf0 0x00 0x0d] +[0x63 0xeb 0x2d 0x46] +# CHECK: and sp, r0, #0 +# CHECK: sbc.w r6, r3, sp, asr #16 + +# STDERR-NOT: warning