1 //===- MipsCallLowering.cpp -------------------------------------*- C++ -*-===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
10 /// This file implements the lowering of LLVM calls to machine code calls for
13 //===----------------------------------------------------------------------===//
15 #include "MipsCallLowering.h"
16 #include "MipsCCState.h"
17 #include "MipsMachineFunction.h"
18 #include "MipsTargetMachine.h"
19 #include "llvm/CodeGen/Analysis.h"
20 #include "llvm/CodeGen/GlobalISel/MachineIRBuilder.h"
24 MipsCallLowering::MipsCallLowering(const MipsTargetLowering
&TLI
)
25 : CallLowering(&TLI
) {}
27 struct MipsOutgoingValueAssigner
: public CallLowering::OutgoingValueAssigner
{
28 /// This is the name of the function being called
29 /// FIXME: Relying on this is unsound
30 const char *Func
= nullptr;
32 /// Is this a return value, or an outgoing call operand.
35 MipsOutgoingValueAssigner(CCAssignFn
*AssignFn_
, const char *Func
,
37 : OutgoingValueAssigner(AssignFn_
), Func(Func
), IsReturn(IsReturn
) {}
39 bool assignArg(unsigned ValNo
, EVT OrigVT
, MVT ValVT
, MVT LocVT
,
40 CCValAssign::LocInfo LocInfo
,
41 const CallLowering::ArgInfo
&Info
, ISD::ArgFlagsTy Flags
,
42 CCState
&State_
) override
{
43 MipsCCState
&State
= static_cast<MipsCCState
&>(State_
);
46 State
.PreAnalyzeReturnValue(EVT::getEVT(Info
.Ty
));
48 State
.PreAnalyzeCallOperand(Info
.Ty
, Info
.IsFixed
, Func
);
50 return CallLowering::OutgoingValueAssigner::assignArg(
51 ValNo
, OrigVT
, ValVT
, LocVT
, LocInfo
, Info
, Flags
, State
);
55 struct MipsIncomingValueAssigner
: public CallLowering::IncomingValueAssigner
{
56 /// This is the name of the function being called
57 /// FIXME: Relying on this is unsound
58 const char *Func
= nullptr;
60 /// Is this a call return value, or an incoming function argument.
63 MipsIncomingValueAssigner(CCAssignFn
*AssignFn_
, const char *Func
,
65 : IncomingValueAssigner(AssignFn_
), Func(Func
), IsReturn(IsReturn
) {}
67 bool assignArg(unsigned ValNo
, EVT OrigVT
, MVT ValVT
, MVT LocVT
,
68 CCValAssign::LocInfo LocInfo
,
69 const CallLowering::ArgInfo
&Info
, ISD::ArgFlagsTy Flags
,
70 CCState
&State_
) override
{
71 MipsCCState
&State
= static_cast<MipsCCState
&>(State_
);
74 State
.PreAnalyzeCallResult(Info
.Ty
, Func
);
76 State
.PreAnalyzeFormalArgument(Info
.Ty
, Flags
);
78 return CallLowering::IncomingValueAssigner::assignArg(
79 ValNo
, OrigVT
, ValVT
, LocVT
, LocInfo
, Info
, Flags
, State
);
84 class MipsIncomingValueHandler
: public CallLowering::IncomingValueHandler
{
85 const MipsSubtarget
&STI
;
88 MipsIncomingValueHandler(MachineIRBuilder
&MIRBuilder
,
89 MachineRegisterInfo
&MRI
)
90 : IncomingValueHandler(MIRBuilder
, MRI
),
91 STI(MIRBuilder
.getMF().getSubtarget
<MipsSubtarget
>()) {}
94 void assignValueToReg(Register ValVReg
, Register PhysReg
,
95 CCValAssign
&VA
) override
;
97 Register
getStackAddress(uint64_t Size
, int64_t Offset
,
98 MachinePointerInfo
&MPO
,
99 ISD::ArgFlagsTy Flags
) override
;
100 void assignValueToAddress(Register ValVReg
, Register Addr
, LLT MemTy
,
101 MachinePointerInfo
&MPO
, CCValAssign
&VA
) override
;
103 unsigned assignCustomValue(CallLowering::ArgInfo
&Arg
,
104 ArrayRef
<CCValAssign
> VAs
) override
;
106 virtual void markPhysRegUsed(unsigned PhysReg
) {
107 MIRBuilder
.getMRI()->addLiveIn(PhysReg
);
108 MIRBuilder
.getMBB().addLiveIn(PhysReg
);
112 class CallReturnHandler
: public MipsIncomingValueHandler
{
114 CallReturnHandler(MachineIRBuilder
&MIRBuilder
, MachineRegisterInfo
&MRI
,
115 MachineInstrBuilder
&MIB
)
116 : MipsIncomingValueHandler(MIRBuilder
, MRI
), MIB(MIB
) {}
119 void markPhysRegUsed(unsigned PhysReg
) override
{
120 MIB
.addDef(PhysReg
, RegState::Implicit
);
123 MachineInstrBuilder
&MIB
;
126 } // end anonymous namespace
128 void MipsIncomingValueHandler::assignValueToReg(Register ValVReg
,
131 markPhysRegUsed(PhysReg
);
132 IncomingValueHandler::assignValueToReg(ValVReg
, PhysReg
, VA
);
135 Register
MipsIncomingValueHandler::getStackAddress(uint64_t Size
,
137 MachinePointerInfo
&MPO
,
138 ISD::ArgFlagsTy Flags
) {
140 MachineFunction
&MF
= MIRBuilder
.getMF();
141 MachineFrameInfo
&MFI
= MF
.getFrameInfo();
143 // FIXME: This should only be immutable for non-byval memory arguments.
144 int FI
= MFI
.CreateFixedObject(Size
, Offset
, true);
145 MPO
= MachinePointerInfo::getFixedStack(MIRBuilder
.getMF(), FI
);
147 return MIRBuilder
.buildFrameIndex(LLT::pointer(0, 32), FI
).getReg(0);
150 void MipsIncomingValueHandler::assignValueToAddress(Register ValVReg
,
151 Register Addr
, LLT MemTy
,
152 MachinePointerInfo
&MPO
,
154 MachineFunction
&MF
= MIRBuilder
.getMF();
155 auto MMO
= MF
.getMachineMemOperand(MPO
, MachineMemOperand::MOLoad
, MemTy
,
156 inferAlignFromPtrInfo(MF
, MPO
));
157 MIRBuilder
.buildLoad(ValVReg
, Addr
, *MMO
);
160 /// Handle cases when f64 is split into 2 32-bit GPRs. This is a custom
161 /// assignment because generic code assumes getNumRegistersForCallingConv is
162 /// accurate. In this case it is not because the type/number are context
163 /// dependent on other arguments.
165 MipsIncomingValueHandler::assignCustomValue(CallLowering::ArgInfo
&Arg
,
166 ArrayRef
<CCValAssign
> VAs
) {
167 const CCValAssign
&VALo
= VAs
[0];
168 const CCValAssign
&VAHi
= VAs
[1];
170 assert(VALo
.getLocVT() == MVT::i32
&& VAHi
.getLocVT() == MVT::i32
&&
171 VALo
.getValVT() == MVT::f64
&& VAHi
.getValVT() == MVT::f64
&&
172 "unexpected custom value");
174 auto CopyLo
= MIRBuilder
.buildCopy(LLT::scalar(32), VALo
.getLocReg());
175 auto CopyHi
= MIRBuilder
.buildCopy(LLT::scalar(32), VAHi
.getLocReg());
177 std::swap(CopyLo
, CopyHi
);
179 Arg
.OrigRegs
.assign(Arg
.Regs
.begin(), Arg
.Regs
.end());
180 Arg
.Regs
= { CopyLo
.getReg(0), CopyHi
.getReg(0) };
181 MIRBuilder
.buildMerge(Arg
.OrigRegs
[0], {CopyLo
, CopyHi
});
183 markPhysRegUsed(VALo
.getLocReg());
184 markPhysRegUsed(VAHi
.getLocReg());
189 class MipsOutgoingValueHandler
: public CallLowering::OutgoingValueHandler
{
190 const MipsSubtarget
&STI
;
193 MipsOutgoingValueHandler(MachineIRBuilder
&MIRBuilder
,
194 MachineRegisterInfo
&MRI
, MachineInstrBuilder
&MIB
)
195 : OutgoingValueHandler(MIRBuilder
, MRI
),
196 STI(MIRBuilder
.getMF().getSubtarget
<MipsSubtarget
>()), MIB(MIB
) {}
199 void assignValueToReg(Register ValVReg
, Register PhysReg
,
200 CCValAssign
&VA
) override
;
202 Register
getStackAddress(uint64_t Size
, int64_t Offset
,
203 MachinePointerInfo
&MPO
,
204 ISD::ArgFlagsTy Flags
) override
;
206 void assignValueToAddress(Register ValVReg
, Register Addr
, LLT MemTy
,
207 MachinePointerInfo
&MPO
, CCValAssign
&VA
) override
;
208 unsigned assignCustomValue(CallLowering::ArgInfo
&Arg
,
209 ArrayRef
<CCValAssign
> VAs
) override
;
211 MachineInstrBuilder
&MIB
;
213 } // end anonymous namespace
215 void MipsOutgoingValueHandler::assignValueToReg(Register ValVReg
,
218 Register ExtReg
= extendRegister(ValVReg
, VA
);
219 MIRBuilder
.buildCopy(PhysReg
, ExtReg
);
220 MIB
.addUse(PhysReg
, RegState::Implicit
);
223 Register
MipsOutgoingValueHandler::getStackAddress(uint64_t Size
,
225 MachinePointerInfo
&MPO
,
226 ISD::ArgFlagsTy Flags
) {
227 MachineFunction
&MF
= MIRBuilder
.getMF();
228 MPO
= MachinePointerInfo::getStack(MF
, Offset
);
230 LLT p0
= LLT::pointer(0, 32);
231 LLT s32
= LLT::scalar(32);
232 auto SPReg
= MIRBuilder
.buildCopy(p0
, Register(Mips::SP
));
234 auto OffsetReg
= MIRBuilder
.buildConstant(s32
, Offset
);
235 auto AddrReg
= MIRBuilder
.buildPtrAdd(p0
, SPReg
, OffsetReg
);
236 return AddrReg
.getReg(0);
239 void MipsOutgoingValueHandler::assignValueToAddress(Register ValVReg
,
240 Register Addr
, LLT MemTy
,
241 MachinePointerInfo
&MPO
,
243 MachineFunction
&MF
= MIRBuilder
.getMF();
244 uint64_t LocMemOffset
= VA
.getLocMemOffset();
246 auto MMO
= MF
.getMachineMemOperand(
247 MPO
, MachineMemOperand::MOStore
, MemTy
,
248 commonAlignment(STI
.getStackAlignment(), LocMemOffset
));
250 Register ExtReg
= extendRegister(ValVReg
, VA
);
251 MIRBuilder
.buildStore(ExtReg
, Addr
, *MMO
);
255 MipsOutgoingValueHandler::assignCustomValue(CallLowering::ArgInfo
&Arg
,
256 ArrayRef
<CCValAssign
> VAs
) {
257 const CCValAssign
&VALo
= VAs
[0];
258 const CCValAssign
&VAHi
= VAs
[1];
260 assert(VALo
.getLocVT() == MVT::i32
&& VAHi
.getLocVT() == MVT::i32
&&
261 VALo
.getValVT() == MVT::f64
&& VAHi
.getValVT() == MVT::f64
&&
262 "unexpected custom value");
265 MIRBuilder
.buildUnmerge({LLT::scalar(32), LLT::scalar(32)}, Arg
.Regs
[0]);
266 Register Lo
= Unmerge
.getReg(0);
267 Register Hi
= Unmerge
.getReg(1);
269 Arg
.OrigRegs
.assign(Arg
.Regs
.begin(), Arg
.Regs
.end());
270 Arg
.Regs
= { Lo
, Hi
};
274 MIRBuilder
.buildCopy(VALo
.getLocReg(), Lo
);
275 MIRBuilder
.buildCopy(VAHi
.getLocReg(), Hi
);
279 static bool isSupportedArgumentType(Type
*T
) {
280 if (T
->isIntegerTy())
282 if (T
->isPointerTy())
284 if (T
->isFloatingPointTy())
289 static bool isSupportedReturnType(Type
*T
) {
290 if (T
->isIntegerTy())
292 if (T
->isPointerTy())
294 if (T
->isFloatingPointTy())
296 if (T
->isAggregateType())
301 bool MipsCallLowering::lowerReturn(MachineIRBuilder
&MIRBuilder
,
302 const Value
*Val
, ArrayRef
<Register
> VRegs
,
303 FunctionLoweringInfo
&FLI
) const {
305 MachineInstrBuilder Ret
= MIRBuilder
.buildInstrNoInsert(Mips::RetRA
);
307 if (Val
!= nullptr && !isSupportedReturnType(Val
->getType()))
310 if (!VRegs
.empty()) {
311 MachineFunction
&MF
= MIRBuilder
.getMF();
312 const Function
&F
= MF
.getFunction();
313 const DataLayout
&DL
= MF
.getDataLayout();
314 const MipsTargetLowering
&TLI
= *getTLI
<MipsTargetLowering
>();
316 SmallVector
<ArgInfo
, 8> RetInfos
;
318 ArgInfo
ArgRetInfo(VRegs
, *Val
, 0);
319 setArgFlags(ArgRetInfo
, AttributeList::ReturnIndex
, DL
, F
);
320 splitToValueTypes(ArgRetInfo
, RetInfos
, DL
, F
.getCallingConv());
322 SmallVector
<CCValAssign
, 16> ArgLocs
;
323 SmallVector
<ISD::OutputArg
, 8> Outs
;
325 MipsCCState
CCInfo(F
.getCallingConv(), F
.isVarArg(), MF
, ArgLocs
,
328 MipsOutgoingValueHandler
RetHandler(MIRBuilder
, MF
.getRegInfo(), Ret
);
329 std::string FuncName
= F
.getName().str();
330 MipsOutgoingValueAssigner
Assigner(TLI
.CCAssignFnForReturn(),
331 FuncName
.c_str(), /*IsReturn*/ true);
333 if (!determineAssignments(Assigner
, RetInfos
, CCInfo
))
336 if (!handleAssignments(RetHandler
, RetInfos
, CCInfo
, ArgLocs
, MIRBuilder
))
340 MIRBuilder
.insertInstr(Ret
);
344 bool MipsCallLowering::lowerFormalArguments(MachineIRBuilder
&MIRBuilder
,
346 ArrayRef
<ArrayRef
<Register
>> VRegs
,
347 FunctionLoweringInfo
&FLI
) const {
349 // Quick exit if there aren't any args.
353 for (auto &Arg
: F
.args()) {
354 if (!isSupportedArgumentType(Arg
.getType()))
358 MachineFunction
&MF
= MIRBuilder
.getMF();
359 const DataLayout
&DL
= MF
.getDataLayout();
360 const MipsTargetLowering
&TLI
= *getTLI
<MipsTargetLowering
>();
362 SmallVector
<ArgInfo
, 8> ArgInfos
;
364 for (auto &Arg
: F
.args()) {
365 ArgInfo
AInfo(VRegs
[i
], Arg
, i
);
366 setArgFlags(AInfo
, i
+ AttributeList::FirstArgIndex
, DL
, F
);
368 splitToValueTypes(AInfo
, ArgInfos
, DL
, F
.getCallingConv());
372 SmallVector
<ISD::InputArg
, 8> Ins
;
374 SmallVector
<CCValAssign
, 16> ArgLocs
;
375 MipsCCState
CCInfo(F
.getCallingConv(), F
.isVarArg(), MF
, ArgLocs
,
378 const MipsTargetMachine
&TM
=
379 static_cast<const MipsTargetMachine
&>(MF
.getTarget());
380 const MipsABIInfo
&ABI
= TM
.getABI();
381 CCInfo
.AllocateStack(ABI
.GetCalleeAllocdArgSizeInBytes(F
.getCallingConv()),
384 const std::string FuncName
= F
.getName().str();
385 MipsIncomingValueAssigner
Assigner(TLI
.CCAssignFnForCall(), FuncName
.c_str(),
387 if (!determineAssignments(Assigner
, ArgInfos
, CCInfo
))
390 MipsIncomingValueHandler
Handler(MIRBuilder
, MF
.getRegInfo());
391 if (!handleAssignments(Handler
, ArgInfos
, CCInfo
, ArgLocs
, MIRBuilder
))
395 ArrayRef
<MCPhysReg
> ArgRegs
= ABI
.GetVarArgRegs();
396 unsigned Idx
= CCInfo
.getFirstUnallocated(ArgRegs
);
399 unsigned RegSize
= 4;
400 if (ArgRegs
.size() == Idx
)
401 VaArgOffset
= alignTo(CCInfo
.getNextStackOffset(), RegSize
);
404 (int)ABI
.GetCalleeAllocdArgSizeInBytes(CCInfo
.getCallingConv()) -
405 (int)(RegSize
* (ArgRegs
.size() - Idx
));
408 MachineFrameInfo
&MFI
= MF
.getFrameInfo();
409 int FI
= MFI
.CreateFixedObject(RegSize
, VaArgOffset
, true);
410 MF
.getInfo
<MipsFunctionInfo
>()->setVarArgsFrameIndex(FI
);
412 for (unsigned I
= Idx
; I
< ArgRegs
.size(); ++I
, VaArgOffset
+= RegSize
) {
413 MIRBuilder
.getMBB().addLiveIn(ArgRegs
[I
]);
414 LLT RegTy
= LLT::scalar(RegSize
* 8);
415 MachineInstrBuilder Copy
=
416 MIRBuilder
.buildCopy(RegTy
, Register(ArgRegs
[I
]));
417 FI
= MFI
.CreateFixedObject(RegSize
, VaArgOffset
, true);
418 MachinePointerInfo MPO
= MachinePointerInfo::getFixedStack(MF
, FI
);
420 const LLT PtrTy
= LLT::pointer(MPO
.getAddrSpace(), 32);
421 auto FrameIndex
= MIRBuilder
.buildFrameIndex(PtrTy
, FI
);
422 MachineMemOperand
*MMO
= MF
.getMachineMemOperand(
423 MPO
, MachineMemOperand::MOStore
, RegTy
, Align(RegSize
));
424 MIRBuilder
.buildStore(Copy
, FrameIndex
, *MMO
);
431 bool MipsCallLowering::lowerCall(MachineIRBuilder
&MIRBuilder
,
432 CallLoweringInfo
&Info
) const {
434 if (Info
.CallConv
!= CallingConv::C
)
437 for (auto &Arg
: Info
.OrigArgs
) {
438 if (!isSupportedArgumentType(Arg
.Ty
))
440 if (Arg
.Flags
[0].isByVal())
442 if (Arg
.Flags
[0].isSRet() && !Arg
.Ty
->isPointerTy())
446 if (!Info
.OrigRet
.Ty
->isVoidTy() && !isSupportedReturnType(Info
.OrigRet
.Ty
))
449 MachineFunction
&MF
= MIRBuilder
.getMF();
450 const Function
&F
= MF
.getFunction();
451 const DataLayout
&DL
= MF
.getDataLayout();
452 const MipsTargetLowering
&TLI
= *getTLI
<MipsTargetLowering
>();
453 const MipsTargetMachine
&TM
=
454 static_cast<const MipsTargetMachine
&>(MF
.getTarget());
455 const MipsABIInfo
&ABI
= TM
.getABI();
457 MachineInstrBuilder CallSeqStart
=
458 MIRBuilder
.buildInstr(Mips::ADJCALLSTACKDOWN
);
460 const bool IsCalleeGlobalPIC
=
461 Info
.Callee
.isGlobal() && TM
.isPositionIndependent();
463 MachineInstrBuilder MIB
= MIRBuilder
.buildInstrNoInsert(
464 Info
.Callee
.isReg() || IsCalleeGlobalPIC
? Mips::JALRPseudo
: Mips::JAL
);
465 MIB
.addDef(Mips::SP
, RegState::Implicit
);
466 if (IsCalleeGlobalPIC
) {
468 MF
.getRegInfo().createGenericVirtualRegister(LLT::pointer(0, 32));
469 MachineInstr
*CalleeGlobalValue
=
470 MIRBuilder
.buildGlobalValue(CalleeReg
, Info
.Callee
.getGlobal());
471 if (!Info
.Callee
.getGlobal()->hasLocalLinkage())
472 CalleeGlobalValue
->getOperand(1).setTargetFlags(MipsII::MO_GOT_CALL
);
473 MIB
.addUse(CalleeReg
);
475 MIB
.add(Info
.Callee
);
476 const TargetRegisterInfo
*TRI
= MF
.getSubtarget().getRegisterInfo();
477 MIB
.addRegMask(TRI
->getCallPreservedMask(MF
, Info
.CallConv
));
479 TargetLowering::ArgListTy FuncOrigArgs
;
480 FuncOrigArgs
.reserve(Info
.OrigArgs
.size());
482 SmallVector
<ArgInfo
, 8> ArgInfos
;
483 for (auto &Arg
: Info
.OrigArgs
)
484 splitToValueTypes(Arg
, ArgInfos
, DL
, Info
.CallConv
);
486 SmallVector
<CCValAssign
, 8> ArgLocs
;
487 bool IsCalleeVarArg
= false;
488 if (Info
.Callee
.isGlobal()) {
489 const Function
*CF
= static_cast<const Function
*>(Info
.Callee
.getGlobal());
490 IsCalleeVarArg
= CF
->isVarArg();
493 // FIXME: Should use MipsCCState::getSpecialCallingConvForCallee, but it
494 // depends on looking directly at the call target.
495 MipsCCState
CCInfo(Info
.CallConv
, IsCalleeVarArg
, MF
, ArgLocs
,
498 CCInfo
.AllocateStack(ABI
.GetCalleeAllocdArgSizeInBytes(Info
.CallConv
),
502 Info
.Callee
.isSymbol() ? Info
.Callee
.getSymbolName() : nullptr;
504 MipsOutgoingValueAssigner
Assigner(TLI
.CCAssignFnForCall(), Call
,
506 if (!determineAssignments(Assigner
, ArgInfos
, CCInfo
))
509 MipsOutgoingValueHandler
ArgHandler(MIRBuilder
, MF
.getRegInfo(), MIB
);
510 if (!handleAssignments(ArgHandler
, ArgInfos
, CCInfo
, ArgLocs
, MIRBuilder
))
513 unsigned NextStackOffset
= CCInfo
.getNextStackOffset();
514 unsigned StackAlignment
= F
.getParent()->getOverrideStackAlignment();
515 if (!StackAlignment
) {
516 const TargetFrameLowering
*TFL
= MF
.getSubtarget().getFrameLowering();
517 StackAlignment
= TFL
->getStackAlignment();
519 NextStackOffset
= alignTo(NextStackOffset
, StackAlignment
);
520 CallSeqStart
.addImm(NextStackOffset
).addImm(0);
522 if (IsCalleeGlobalPIC
) {
523 MIRBuilder
.buildCopy(
525 MF
.getInfo
<MipsFunctionInfo
>()->getGlobalBaseRegForGlobalISel(MF
));
526 MIB
.addDef(Mips::GP
, RegState::Implicit
);
528 MIRBuilder
.insertInstr(MIB
);
529 if (MIB
->getOpcode() == Mips::JALRPseudo
) {
530 const MipsSubtarget
&STI
=
531 static_cast<const MipsSubtarget
&>(MIRBuilder
.getMF().getSubtarget());
532 MIB
.constrainAllUses(MIRBuilder
.getTII(), *STI
.getRegisterInfo(),
533 *STI
.getRegBankInfo());
536 if (!Info
.OrigRet
.Ty
->isVoidTy()) {
539 CallLowering::splitToValueTypes(Info
.OrigRet
, ArgInfos
, DL
,
542 const std::string FuncName
= F
.getName().str();
543 SmallVector
<ISD::InputArg
, 8> Ins
;
544 SmallVector
<CCValAssign
, 8> ArgLocs
;
545 MipsIncomingValueAssigner
Assigner(TLI
.CCAssignFnForReturn(),
548 CallReturnHandler
RetHandler(MIRBuilder
, MF
.getRegInfo(), MIB
);
550 MipsCCState
CCInfo(F
.getCallingConv(), F
.isVarArg(), MF
, ArgLocs
,
553 if (!determineAssignments(Assigner
, ArgInfos
, CCInfo
))
556 if (!handleAssignments(RetHandler
, ArgInfos
, CCInfo
, ArgLocs
, MIRBuilder
))
560 MIRBuilder
.buildInstr(Mips::ADJCALLSTACKUP
).addImm(NextStackOffset
).addImm(0);