Bug 12569 - tailcall opcode bug in arm (?)
Summary: tailcall opcode bug in arm (?)
Status: RESOLVED NOT_ON_ROADMAP
Alias: None
Product: Android
Classification: Xamarin
Component: Mono runtime / AOT Compiler ()
Version: 4.8.x
Hardware: PC Windows
: Low enhancement
Target Milestone: ---
Assignee: Rodrigo Kumpera
URL:
Depends on:
Blocks:
 
Reported: 2013-06-05 20:40 UTC by soywiz
Modified: 2016-04-14 05:49 UTC (History)
2 users (show)

Tags:
Is this bug a regression?: ---
Last known good build:

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.


Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:
Status:
RESOLVED NOT_ON_ROADMAP

Description soywiz 2013-06-05 20:40:27 UTC
I'm creating an emulator that performs dynamic recompilation. In some cases it performs a call that is using the IL tailcall prefix to perform a jump between functions.

The process terminates with this output and without outputing anything else (no stacktraces or more information):
"D/Zygote  (26133): Process 8835 terminated by signal (11)"

It can be reproduced 100% of the time (when it is running by a few seconds) and maybe I could create a test case. It fails just on android, it works fine with ms.net and mono for windows; so maybe it is related to ARM?
Maybe instead performing a tailcall it is just calling the method directly and thus it yields to a stackoverflow since I'm doing a lot of jumps continuously.

This is the piece of related code (with NATIVE_JUMPS setted):
static public DynarecResult JP(DynarecContextChip8 Context, ushort Address)
{
	return ast.Statements(
#if NATIVE_JUMPS
		ast.Statement(ast.CallTail(ast.CallDelegate(Context.GetCallForAddress(Address), Context.GetCpuContext()))),
#else
		ast.Statements(ast.Assign(Context.GetPC(), Address)),
#endif
		ast.Return()
	);
}

It generates something like:

  ldarg.0
  ldc.i4 572
  call System.Action`1[CSharpCpu.Cpus.Chip8.CpuContext] GetDelegateForAddress(UInt32)
  ldarg.0
  tail.
  call Void Invoke(CSharpCpu.Cpus.Chip8.CpuContext)
  ret
Comment 1 Rodrigo Kumpera 2013-06-06 11:37:23 UTC
Please provide a self contained test case (an android solution) that shows the issue.
Comment 2 soywiz 2013-06-06 16:33:39 UTC
I am running the emulator in windows, and it works both with ms.net and with mono 3.0.10. Emulator has a lot of code so it is hard to provide a test case. Within android, app crashes within 2 seconds, and on windows with mono I have been running it for minutes without any crash.

The following example seems to fail in mono for windows too, but it works on ms.net (when not debugging).
With this example, the android app (and mono for windows too) is just crashing even when not debugging. Tailcall should remove all the stack from the current function, so performing a call with the tailcall prefix, should be equivalent to performing a plain Jump.

//#define STACK_OVERFLOW

using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

unsafe public class Program
{
	static private Action MyAction1;
	static private Action MyAction2;

	static public Action GetTestFunc1()
	{
		Console.Write("[1]");
		return MyAction1;
	}
	static public Action GetTestFunc2()
	{
		Console.Write("[2]");
		return MyAction2;
	}

	static Action Generate(string GetFunc)
	{
		var DynamicMethod = new DynamicMethod(
			"Test" + GetFunc,
			typeof(void),
			new Type[] { },
			Assembly.GetExecutingAssembly().ManifestModule
		);
		var ILGenerator = DynamicMethod.GetILGenerator();
		ILGenerator.Emit(OpCodes.Call, typeof(Program).GetMethod(GetFunc));
#if !STACK_OVERFLOW
		ILGenerator.Emit(OpCodes.Tailcall);
#endif
		ILGenerator.Emit(OpCodes.Call, typeof(Action).GetMethod("Invoke"));
		ILGenerator.Emit(OpCodes.Ret);
		return (Action)DynamicMethod.CreateDelegate(typeof(Action));
	}

	static void Main(string[] args)
	{
		new Thread(() =>
		{
			MyAction2 = Generate("GetTestFunc1");
			MyAction1 = Generate("GetTestFunc2");
			MyAction1();
		}).Start();
	}
}

//////////////////////////////
The Android APP just based on ICS App template:

using System;
using Android.App;
using Android.Content;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Android.OS;
using System.Reflection.Emit;
using System.Reflection;
using System.Threading;

namespace android1
{
	[Activity (Label = "android1", MainLauncher = true)]
	public class Activity1 : Activity
	{
		int count = 1;

		static private Action MyAction;

		static public Action GetTestFunc()
		{
			return MyAction;
		}

		static void Run()
		{
			var DynamicMethod = new DynamicMethod(
				"Test",
				typeof(void),
				new Type[] { },
			Assembly.GetExecutingAssembly().ManifestModule
			);
			var ILGenerator = DynamicMethod.GetILGenerator();
			ILGenerator.Emit(OpCodes.Call, typeof(Activity1).GetMethod("GetTestFunc"));
			#if !STACK_OVERFLOW
			ILGenerator.Emit(OpCodes.Tailcall);
			#endif
			ILGenerator.Emit(OpCodes.Call, typeof(Action).GetMethod("Invoke"));
			ILGenerator.Emit(OpCodes.Ret);
			MyAction = (Action)DynamicMethod.CreateDelegate(typeof(Action));
			MyAction();
		}

		protected override void OnCreate (Bundle bundle)
		{
			base.OnCreate (bundle);

			// Set our view from the "main" layout resource
			SetContentView (Resource.Layout.Main);

			new Thread (Run).Start ();

			// Get our button from the layout resource,
			// and attach an event to it
			//Button button = FindViewById<Button> (Resource.Id.myButton);
			
			//button.Click += delegate {
			//	button.Text = string.Format ("{0} clicks!", count++);
			//};
		}
	}
}
Comment 3 Rodrigo Kumpera 2013-06-06 18:46:34 UTC
Zoltan, do we support tail calls over delegates?
Comment 4 Rodrigo Kumpera 2013-06-06 18:46:53 UTC
Test was provided.
Comment 5 Zoltan Varga 2013-06-07 02:14:27 UTC
Our tail call support is not very good on ARM.
Comment 6 Rodrigo Kumpera 2013-08-26 13:15:45 UTC
We have no plans on working on tailcalls improvements for now.
Comment 7 Rodrigo Kumpera 2016-04-14 05:49:30 UTC
Closing this bug as we have no plans to improve on the current set of limitations for tail calls on arm.