@@ -107,13 +107,40 @@ value rocq_tcode_array(value tcodes) {
107107 CAMLreturn (res );
108108}
109109
110- CAMLprim value rocq_obj_set_tag (value arg , value new_tag )
111- {
112- #if OCAML_VERSION >= 50000
113- // Placeholder used by native_compute
114- abort ();
110+ code_t rocq_accumulate_addr ;
111+
112+ #if defined(__GNUC__ ) && defined(__amd64__ )
113+ value rocq_proxy_accu (value clos ) {
114+ value v ;
115+ CAMLassert (Tag_val (clos ) == Closure_tag && Arity_closinfo (Closinfo_val (clos )) == 2 );
116+ /* Field 2 of the closure contains the code pointer for the arity-2 direct call. */
117+ rocq_accumulate_addr = ((code_t * )clos )[2 ];
118+ /* The following assembly block does not perform any meaningful computation;
119+ it just returns a pointer to the inner code (notice the initial "jmp").
120+ The inner code translates the call "foo x" (i.e., "%apply x foo") into
121+ "accumulate foo.2 x". For both calls, the two arguments are stored in %rax
122+ and %rbx, while register %rdi is caller-saved and hence usable. */
123+ asm("jmp 1f\n\t"
124+ ".align 8\n\t"
125+ ".quad 3067\n"
126+ "2:\n\t"
127+ "mov %%rax, %%rdi\n\t"
128+ "mov 16(%%rbx), %%rax\n\t"
129+ "mov %%rdi, %%rbx\n\t"
130+ "mov rocq_accumulate_addr@GOTPCREL(%%rip), %%rdi\n\t"
131+ "jmp *(%%rdi)\n"
132+ "1:\n\t"
133+ "lea 2b(%%rip), %0\n\t"
134+ : "=r" (v ));
135+ /* v is a pointer that can be used as field 0 of an OCaml closure. But it is
136+ also a pointer to a block that is ignored by the garbage collector (notice
137+ the header 3067). So, v can be put inside closures that do not have tag 247. */
138+ value r = caml_alloc_small (1 , 0 );
139+ Field (r , 0 ) = v ;
140+ return r ;
141+ }
115142#else
116- Tag_val (arg ) = Int_val (new_tag );
117- #endif
118- return Val_unit ;
143+ value rocq_proxy_accu (value ) {
144+ return 1 ;
119145}
146+ #endif
0 commit comments