# Interpreter, backtrace, and call site caching

**URL:** <https://www.ruby-forum.com/t/interpreter-backtrace-and-call-site-caching/156613>\
**Category:** IronRuby\
**Created:** [December 30, 2008, 11:28pm UTC](https://www.ruby-forum.com/t/interpreter-backtrace-and-call-site-caching/156613 "2008-12-30T23:28:22Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![Shri\_B](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/898d66/32.png) [@Shri\_B](https://www.ruby-forum.com/u/Shri_B)\
**Post date:** [December 30, 2008, 11:28pm UTC](https://www.ruby-forum.com/t/interpreter-backtrace-and-call-site-caching/156613/1 "2008-12-30T23:28:22Z")

</div>

For backtraces to work correctly in interpreter mode, it is required  
that the interpreter guard every call to C# code with a try-catch, so  
that the catch block has a chance to stash away the backtrace if an  
exception is thrown. This is done in Interpreter.InvokeMethod, and the  
catch block gives the language a chance to save the backtrace by calling  
LanguageContext.InterpretExceptionThrow.

However, with call-site caching enabled in interpreter mode,  
Interpreter.InterpretMetaAction can directl invoke the compiled delegate  
instead of calling Interpreter.InvokeMethod. This breaks the backtrace.

```
    private static object InterpretMetaAction(InterpreterState 

```

state, DynamicMetaObjectBinder action, DynamicExpression node, object[]  
argValues) {  
…

```
        callSiteInfo.Counter++;
        if (callSiteInfo.Counter > SiteCompileThreshold) {
            if (callSiteInfo.CallSite == null) {
                SetCallSite(callSiteInfo, node);
            }
            return callSiteInfo.CallerTarget(callSiteInfo.CallSite, 

```

argValues);  
}

…  
var result = Interpret(state, binding.Expression);  
return result;  
}

Is this a known issue? The interpreter stack trace in the unoptimized  
case is shown below. The fix could be for the interpreter to maintain a  
separate cache of compiled rules which have a try-catch generated for  
all MethodCallExpression nodes so as to match the unoptimized code  
behavior.

Thanks,  
Shri

```
            Microsoft.Scripting.dll!Microsoft.Scripting.Utils.ReflectedCaller.Invoke(object[] 

```

args = {object[0x00000001]}) Line 46 + 0x19 bytes C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InvokeMethod(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Reflection.MethodInfo method =  
{System.Reflection.RuntimeMethodInfo}, object instance = null, object[]  
parameters = {object[0x00000001]}) Line 135 + 0xb bytes C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretMethodCallExpression(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression expr =  
{System.Linq.Expressions.MethodCallExpressionN}) Line 251 + 0x30 bytes  
C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.Interpret(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression expr =  
{System.Linq.Expressions.MethodCallExpressionN}) Line 38 + 0xb bytes  
C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretAndCheckFlow(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression node =  
{System.Linq.Expressions.MethodCallExpressionN}, out object result =  
null) Line 76 + 0x11 bytes C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretBlockExpression(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression expr =  
{System.Linq.Expressions.Block2}) Line 1256 + 0xf bytes  
C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.Interpret(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression expr =  
{System.Linq.Expressions.Block2}) Line 79 + 0xb bytes C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretAndCheckYield(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression target =  
{System.Linq.Expressions.Block2}, out object res = true) Line 86 + 0x11  
bytes C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretConditionalExpression(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression expr =  
{System.Linq.Expressions.FullConditionalExpression}) Line 113 + 0x22  
bytes C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.Interpret(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression expr =  
{System.Linq.Expressions.FullConditionalExpression}) Line 40 + 0xb bytes  
C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretMetaAction(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Dynamic.DynamicMetaObjectBinder action =  
{IronRuby.Runtime.Calls.RubyCallAction},  
System.Linq.Expressions.DynamicExpression node =  
{System.Linq.Expressions.DynamicExpression2}, object[] argValues =  
{object[0x00000002]}) Line 810 + 0x2a bytes C#

---

<div class="post-metadata">

**Author:** ![Shri\_B](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/s/898d66/32.png) [@Shri\_B](https://www.ruby-forum.com/u/Shri_B)\
**Post date:** [December 31, 2008, 9:07am UTC](https://www.ruby-forum.com/t/interpreter-backtrace-and-call-site-caching/156613/2 "2008-12-31T09:07:10Z")

</div>

Adding a try-catch around the optimized case (callSiteInfo.CallerTarget)  
fixes the problem. However, this points out that we need to have a  
try-catch around every operation in Interpreter.cs that could cause an  
exception and have the catch call  
LanguageContext.InterpretExceptionThrow.

From: removed\_email\_address@domain.invalid  
[[mailto:removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid)] On Behalf Of Shri B.  
Sent: Tuesday, December 30, 2008 2:28 PM  
To: removed\_email\_address@domain.invalid  
Subject: [Ironruby-core] Interpreter, backtrace, and call site caching

For backtraces to work correctly in interpreter mode, it is required  
that the interpreter guard every call to C# code with a try-catch, so  
that the catch block has a chance to stash away the backtrace if an  
exception is thrown. This is done in Interpreter.InvokeMethod, and the  
catch block gives the language a chance to save the backtrace by calling  
LanguageContext.InterpretExceptionThrow.

However, with call-site caching enabled in interpreter mode,  
Interpreter.InterpretMetaAction can directl invoke the compiled delegate  
instead of calling Interpreter.InvokeMethod. This breaks the backtrace.

```
    private static object InterpretMetaAction(InterpreterState 

```

state, DynamicMetaObjectBinder action, DynamicExpression node, object[]  
argValues) {  
…

```
        callSiteInfo.Counter++;
        if (callSiteInfo.Counter > SiteCompileThreshold) {
            if (callSiteInfo.CallSite == null) {
                SetCallSite(callSiteInfo, node);
            }
            return callSiteInfo.CallerTarget(callSiteInfo.CallSite, 

```

argValues);  
}

…  
var result = Interpret(state, binding.Expression);  
return result;  
}

Is this a known issue? The interpreter stack trace in the unoptimized  
case is shown below. The fix could be for the interpreter to maintain a  
separate cache of compiled rules which have a try-catch generated for  
all MethodCallExpression nodes so as to match the unoptimized code  
behavior.

Thanks,  
Shri

```
            Microsoft.Scripting.dll!Microsoft.Scripting.Utils.ReflectedCaller.Invoke(object[] 

```

args = {object[0x00000001]}) Line 46 + 0x19 bytes C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InvokeMethod(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Reflection.MethodInfo method =  
{System.Reflection.RuntimeMethodInfo}, object instance = null, object[]  
parameters = {object[0x00000001]}) Line 135 + 0xb bytes C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretMethodCallExpression(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression expr =  
{System.Linq.Expressions.MethodCallExpressionN}) Line 251 + 0x30 bytes  
C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.Interpret(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression expr =  
{System.Linq.Expressions.MethodCallExpressionN}) Line 38 + 0xb bytes  
C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretAndCheckFlow(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression node =  
{System.Linq.Expressions.MethodCallExpressionN}, out object result =  
null) Line 76 + 0x11 bytes C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretBlockExpression(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression expr =  
{System.Linq.Expressions.Block2}) Line 1256 + 0xf bytes  
C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.Interpret(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression expr =  
{System.Linq.Expressions.Block2}) Line 79 + 0xb bytes C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretAndCheckYield(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression target =  
{System.Linq.Expressions.Block2}, out object res = true) Line 86 + 0x11  
bytes C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretConditionalExpression(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression expr =  
{System.Linq.Expressions.FullConditionalExpression}) Line 113 + 0x22  
bytes C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.Interpret(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Linq.Expressions.Expression expr =  
{System.Linq.Expressions.FullConditionalExpression}) Line 40 + 0xb bytes  
C#  
Microsoft.Scripting.dll!Microsoft.Scripting.Interpretation.Interpreter.InterpretMetaAction(Microsoft.Scripting.Interpretation.InterpreterState  
state = {Microsoft.Scripting.Interpretation.InterpreterState},  
System.Dynamic.DynamicMetaObjectBinder action =  
{IronRuby.Runtime.Calls.RubyCallAction},  
System.Linq.Expressions.DynamicExpression node =  
{System.Linq.Expressions.DynamicExpression2}, object[] argValues =  
{object[0x00000002]}) Line 810 + 0x2a bytes C#
