diff --git a/src/coreclr/interpreter/compiler.cpp b/src/coreclr/interpreter/compiler.cpp index 45d931af5529aa..dea39ad1a35c3b 100644 --- a/src/coreclr/interpreter/compiler.cpp +++ b/src/coreclr/interpreter/compiler.cpp @@ -2233,7 +2233,7 @@ void InterpCompiler::CreateBasicBlocks(CORINFO_METHOD_INFO* methodInfo) default: assert(0); } - if (opcode == CEE_THROW || opcode == CEE_ENDFINALLY || opcode == CEE_RETHROW) + if (opcode == CEE_THROW || opcode == CEE_ENDFINALLY || opcode == CEE_RETHROW || opcode == CEE_JMP) GetBB((int32_t)(ip - codeStart)); } } @@ -3900,6 +3900,7 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re bool isVirtual = (*m_ip == CEE_CALLVIRT); bool isDelegateInvoke = false; + bool isJmp = (*m_ip == CEE_JMP); CORINFO_RESOLVED_TOKEN resolvedCallToken; CORINFO_CALL_INFO callInfo; @@ -3943,6 +3944,16 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re BADCODE("Vararg methods are not supported in interpreted code"); } + if (isJmp) + { + if (callInfo.sig.numArgs != m_methodInfo->args.numArgs || + callInfo.sig.retType != m_methodInfo->args.retType || + callInfo.sig.callConv != m_methodInfo->args.callConv) + { + BADCODE("Incompatible target for CEE_JMP"); + } + } + // Inject call to callsite callout helper EmitCallsiteCallout(callInfo.accessAllowed, &callInfo.callsiteCalloutHelper); @@ -4031,6 +4042,16 @@ void InterpCompiler::EmitCall(CORINFO_RESOLVED_TOKEN* pConstrainedToken, bool re CORINFO_ARG_LIST_HANDLE args; args = callInfo.sig.args; + if (isJmp) + { + assert(tailcall); + // CEE_JMP is simulated as a tail call, so we need to load the current method's args + for (int i = 0; i < numArgsFromStack; i++) + { + EmitLoadVar(i); + } + } + for (int iActualArg = 0, iLogicalArg = 0; iActualArg < numArgs; iActualArg++) { if (iActualArg == extraParamArgLocation) @@ -6929,6 +6950,18 @@ void InterpCompiler::GenerateCode(CORINFO_METHOD_INFO* methodInfo) EmitUnaryArithmeticOp(INTOP_NOT_I4); m_ip++; break; + case CEE_JMP: + { + CHECK_STACK(0); + if (m_pCBB->clauseType != BBClauseNone) + { + // CEE_JMP inside a funclet is not allowed + BADCODE("CEE_JMP inside funclet"); + } + EmitCall(pConstrainedToken, readonly, true /* tailcall */, false /*newObj*/, false /*isCalli*/); + linkBBlocks = false; + break; + } case CEE_CALLVIRT: case CEE_CALL: EmitCall(pConstrainedToken, readonly, tailcall, false /*newObj*/, false /*isCalli*/); diff --git a/src/tests/JIT/Directed/Directed_1.csproj b/src/tests/JIT/Directed/Directed_1.csproj index 66ec17b4004a62..f67d9fc445dacd 100644 --- a/src/tests/JIT/Directed/Directed_1.csproj +++ b/src/tests/JIT/Directed/Directed_1.csproj @@ -2,6 +2,7 @@ + diff --git a/src/tests/JIT/Directed/Directed_3.csproj b/src/tests/JIT/Directed/Directed_3.csproj index 22f7e7688b5fd7..52a10d33386556 100644 --- a/src/tests/JIT/Directed/Directed_3.csproj +++ b/src/tests/JIT/Directed/Directed_3.csproj @@ -5,6 +5,7 @@ + diff --git a/src/tests/JIT/Directed/jmp/genericjmp.il b/src/tests/JIT/Directed/jmp/genericjmp.il new file mode 100644 index 00000000000000..e0ed32fd94a1d5 --- /dev/null +++ b/src/tests/JIT/Directed/jmp/genericjmp.il @@ -0,0 +1,112 @@ +.assembly JmpGenericTest {} +.assembly extern System.Console +{ + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) + .ver 0:0:0:0 +} +.assembly extern System.Runtime +{ + .publickeytoken = (B0 3F 5F 7F 11 D5 0A 3A ) + .ver 0:0:0:0 +} +.assembly extern xunit.core {} +.assembly extern xunit.assert {} + +.assembly genericjmp { } + +.class public auto ansi beforefieldinit JmpGenericToGeneric extends [System.Runtime]System.Object +{ + .method public static void Target() cil managed + { + .maxstack 8 + ldstr "Target called" + call void [System.Console]System.Console::WriteLine(string) + ret + } + + .method public static void Source() cil managed + { + jmp void JmpGenericToGeneric::Target() + } + + .method public static void TestEntryPoint() cil managed + { + .custom instance void [xunit.core]Xunit.FactAttribute::.ctor() = ( + 01 00 00 00 + ) + call void JmpGenericToGeneric::Source() + ret + } +} + +.class public auto ansi beforefieldinit JmpGenericToRegular extends [System.Runtime]System.Object +{ + .method public static void Target() cil managed + { + .maxstack 8 + ldstr "Regular Target called" + call void [System.Console]System.Console::WriteLine(string) + ret + } + + .method public static void Source() cil managed + { + jmp void JmpGenericToRegular::Target() + } + + .method public static void TestEntryPoint() cil managed + { + .custom instance void [xunit.core]Xunit.FactAttribute::.ctor() = ( + 01 00 00 00 + ) + .try + { + call void JmpGenericToRegular::Source() + ldstr "JMP from generic to regular method is not allowed" + call void [xunit.assert]Xunit.Assert::Fail(string) + leave END + } + catch [System.Runtime]System.InvalidProgramException + { + leave END + } + END: + ret + } +} + +.class public auto ansi beforefieldinit JmpRegularToGeneric extends [System.Runtime]System.Object +{ + .method public static void Target() cil managed + { + .maxstack 8 + ldstr "Generic Target called" + call void [System.Console]System.Console::WriteLine(string) + ret + } + + .method public static void Source() cil managed + { + jmp void JmpRegularToGeneric::Target() + } + + .method public static void TestEntryPoint() cil managed + { + .custom instance void [xunit.core]Xunit.FactAttribute::.ctor() = ( + 01 00 00 00 + ) + .try + { + call void JmpRegularToGeneric::Source() + ldstr "JMP from regular to generic method is not allowed" + call void [xunit.assert]Xunit.Assert::Fail(string) + leave END + } + catch [System.Runtime]System.InvalidProgramException + { + leave END + } + END: + ret + } +} \ No newline at end of file diff --git a/src/tests/JIT/Directed/jmp/genericjmp.ilproj b/src/tests/JIT/Directed/jmp/genericjmp.ilproj new file mode 100644 index 00000000000000..e30982fc15569e --- /dev/null +++ b/src/tests/JIT/Directed/jmp/genericjmp.ilproj @@ -0,0 +1,9 @@ + + + Full + 1 + + + + +