1 --------------------------------------------------------------------
5 Initial tests are imported from coreclr roughly via:
8 git clone https://github.com/dotnet/coreclr # 66f840939e81a6e240a01edfea61976363bc51d6
9 cd /dev2/mono/mono/tests
10 mkdir -p tailcall/coreclr
12 cp -prv /dev2/coreclr/tests/src/* .
13 find . | grep proj$ | xargs rm
14 find . | grep -vi tail | xargs rm
18 and that this point optional:
19 for a in `find . -type f`; do if [ ! -e $(basename $a) ] ; then mv $a $(basename $a) ; fi done
21 find . | grep -vi tail | xargs rmdir
24 and then optionally pick up stragglers.
26 --------------------------------------------------------------------
29 There a few buckets of known behavior in mono and .NET regarding tail calls.
31 Intuition is only partly correct.
33 Intuitively, a tail call can be performed when the outgoing parameters
34 match incoming parameters in "location" (specific register or stack location)
35 and count. Some wiggle room could be afforded for integer types -- signedess
36 would not matter, nor exact size, as long as fits in a regster.
38 Passing the address of a local would seem disallowed, however might be allowed
39 within the red zone? This is mentioned in ECMA.
41 Managed-pointer-ness would be helpful to match up, but this requirement
42 depends flexible details -- as long as GC info is accurate and/or GC is not
43 allowed while a location changes, ok.
45 Exception handling would likely get in the way -- you can't have a handler in scope
46 if your frame has been torn down, as exception handling needs a way
47 to know your scope is live -- either via RIP or a thread local linked list.
48 As well, this is mentioned in ECMA.
50 Mono does not tailcall virtual calls.
51 No intuition assists me here.
52 FIXME: Specific test cases that demonstrate this.
54 Mono does not tailcall "something about generics, generic value types, generic context".
55 No intuition assists me here.
56 FIXME: Specific test cases that demonstrate this.
58 .NET goes very far out of its way to allow the outgoing and incoming signatures
59 to vary arbitrarily, including growing the parameter list unboundedly.
60 This is very surprising. There is a specification (FIXME) to do this for CoreCLR/Unix,
61 however CoreCLR/Unix presently does not do this.
63 --------------------------------------------------------------------
67 The imported CoreCLR tailcall tests have a variety of good and bad aspects.
70 - Some have explicit tail calls.
71 - Some have non demarked but easily optimized calls.
72 - Some can be run on Unix. Some cannot -- p/invoke to user32.dll.
73 - P/invoke is a useful portably interesting variation, even if user32.dll is not portable.
74 - Most can be built stand alone, but not all.
76 Some succeeded whether or not a tailcall optimization is done.
78 Some run out of stack, at least in the absence of tailcall.
79 - Some use generics, some do not -- at least the first F# test (citation needed).
81 These variations make for usual simple run/don't-run, or succeed/fail partitioning of the tests
84 Due to the fact that many are not runnable, it becomes desirable to
85 use mono --compile-all switch, which will not run the code, just JIT it.
87 However this switch skips generics.
89 Therefore, we must either fix this switch, and/or develop new runnable portable tests,
90 that clearly indicate their success or failure, with an exit code.
92 It is important to consider the CoreCLR tests as just a starting point, and not the
93 desired end state of tailcall tests.
95 --------------------------------------------------------------------
99 An approximate test plan should be:
100 generate a combinatorial series of tests with the following variables
102 - tailcall in try and outside of try
103 Inside try cannot be optimized.
104 - tailcall in except and outside of except
105 Inside except cannot be optimized?
106 - tailcall with exactly matching signatures
107 Exactly matching is easier. Non-matching varies in ease.
108 - tailcall with non matching signatures, but for which all parameters are in registers
109 Same registers is easy.
110 - tailcall with all parameters in registers and tailcall using stack for registers
111 All registers should be easy, no matter the precise signature.
112 Same stack should be easy.
113 Varying stack, bigger or smaller, is very difficult but not impossible.
114 - tailcall with managed pointers and non-managed pointers (integers)
115 - tailcall with integer and tailcall with float
116 - tailcall where parameters includes a passthrough ref parameter
117 - tailcall to same function and tailcall to other function
118 - Same function is particularly trivial and less interesting.
120 generic reference types
121 non-generic reference types
123 non-generic value types
125 non-reference typess -- integer, float, value
126 virtual and non-virtual
127 static and non-static
128 varargs and non-varargs (how to construct varargs?)
129 p/invoke and non-p/invoke
130 call to same assembly and call to outside assembly (ECMA discerns this)