@@ -1817,6 +1817,129 @@ def test_multiple_labels(self):
1817
1817
}
1818
1818
"""
1819
1819
1820
+ class TestGeneratedTailCallHandlers (unittest .TestCase ):
1821
+ def setUp (self ) -> None :
1822
+ super ().setUp ()
1823
+ self .maxDiff = None
1824
+
1825
+ self .temp_dir = tempfile .gettempdir ()
1826
+ self .temp_input_filename = os .path .join (self .temp_dir , "input.txt" )
1827
+ self .temp_output_filename = os .path .join (self .temp_dir , "output.txt" )
1828
+ self .temp_labels_output_filename = os .path .join (self .temp_dir , "labels_output.txt" )
1829
+
1830
+ def tearDown (self ) -> None :
1831
+ for filename in [
1832
+ self .temp_input_filename ,
1833
+ self .temp_output_filename ,
1834
+ self .temp_labels_output_filename ,
1835
+ ]:
1836
+ try :
1837
+ os .remove (filename )
1838
+ except Exception :
1839
+ pass
1840
+ super ().tearDown ()
1841
+
1842
+ def run_cases_test (self , input : str , expected : str , expected_labels : str ):
1843
+ with open (self .temp_input_filename , "w+" ) as temp_input :
1844
+ temp_input .write (parser .BEGIN_MARKER )
1845
+ temp_input .write (input )
1846
+ temp_input .write (parser .END_MARKER )
1847
+ temp_input .flush ()
1848
+
1849
+ with handle_stderr ():
1850
+ tier1_tail_call_generator .generate_tier1_from_files (
1851
+ [self .temp_input_filename ], self .temp_output_filename , self .temp_labels_output_filename , False
1852
+ )
1853
+
1854
+ with open (self .temp_output_filename ) as temp_output :
1855
+ lines = temp_output .read ()
1856
+ _ , rest = lines .split (tier1_generator .INSTRUCTION_START_MARKER )
1857
+ instructions , _ = rest .split (tier1_generator .INSTRUCTION_END_MARKER )
1858
+
1859
+ with open (self .temp_labels_output_filename ) as temp_output :
1860
+ lines = temp_output .readlines ()
1861
+ while lines and lines [0 ].startswith (("// " , "#" , " #" , "\n " )):
1862
+ lines .pop (0 )
1863
+ while lines and lines [- 1 ].startswith (("#" , "\n " )):
1864
+ lines .pop (- 1 )
1865
+ actual_labels = "" .join (lines )
1866
+
1867
+ self .assertEqual (instructions .strip (), expected .strip ())
1868
+ self .assertEqual (actual_labels .strip (), expected_labels .strip ())
1869
+
1870
+ def test_basic (self ):
1871
+ input = """
1872
+ inst(OP, (--)) {
1873
+ SPAM();
1874
+ }
1875
+ """
1876
+ output = """
1877
+ Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_OP(TAIL_CALL_PARAMS) {
1878
+ {
1879
+ frame->instr_ptr = next_instr;
1880
+ next_instr += 1;
1881
+ INSTRUCTION_STATS(OP);
1882
+ SPAM();
1883
+ }
1884
+ DISPATCH();
1885
+ }
1886
+ """
1887
+ output_labels = ""
1888
+ self .run_cases_test (input , output , output_labels )
1889
+
1890
+ def test_label_transformed (self ):
1891
+ """This tests that the labels and their gotos/DISPATCH get transformed to tail calls in the
1892
+ tail-calling interpreter, while staying the same/becoming an entry call in the normal interpreter.
1893
+ """
1894
+ input = """
1895
+ inst(OP, (--)) {
1896
+ SPAM();
1897
+ }
1898
+
1899
+ label(hello) {
1900
+ EGGS();
1901
+ if (x) {
1902
+ goto baz;
1903
+ }
1904
+ DISPATCH();
1905
+ }
1906
+ """
1907
+ output = """
1908
+ Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_hello(TAIL_CALL_PARAMS)
1909
+ {
1910
+ EGGS();
1911
+ if (x) {
1912
+ TAIL_CALL(baz);
1913
+ }
1914
+ DISPATCH();
1915
+ }
1916
+
1917
+
1918
+ Py_PRESERVE_NONE_CC static PyObject *_TAIL_CALL_OP(TAIL_CALL_PARAMS) {
1919
+ {
1920
+ frame->instr_ptr = next_instr;
1921
+ next_instr += 1;
1922
+ INSTRUCTION_STATS(OP);
1923
+ SPAM();
1924
+ }
1925
+ DISPATCH();
1926
+ hello:
1927
+ TAIL_CALL(hello);
1928
+ }
1929
+ """
1930
+ output_labels = """
1931
+ hello:
1932
+ {
1933
+ EGGS();
1934
+ if (x) {
1935
+ goto baz;
1936
+ }
1937
+ return _TAIL_CALL_entry(frame, stack_pointer, tstate, next_instr, 0, 0);
1938
+ }
1939
+ """
1940
+ self .run_cases_test (input , output , output_labels )
1941
+
1942
+
1820
1943
class TestGeneratedAbstractCases (unittest .TestCase ):
1821
1944
def setUp (self ) -> None :
1822
1945
super ().setUp ()
0 commit comments