@@ -107,38 +107,75 @@ value rocq_tcode_array(value tcodes) {
107107 CAMLreturn (res );
108108}
109109
110+ /* The following architecture-dependent function returns a core pointer
111+ (wrapped in an option) that can be used as field 0 of an OCaml closure.
112+ But since the word before the start of the code contains a header that
113+ causes it to be ignored by the garbage collector, the pointer can even be
114+ put inside closures that do not have tag 247.
115+
116+ The code itself translates the call "foo x" (i.e., "%apply x foo") into
117+ "accumulate foo.2 x". The first few instructions are those generated by
118+ the OCaml compiler for the closure returned by the following partial
119+ application:
120+
121+ let foo data = accumulate data (* in Nativevalues *)
122+
123+ The last few instructions invoke the code pointer stored in rocq_accumulate_addr.
124+ This variable is initialized using field 2 of the closure passed as parameter
125+ (i.e., "accumulate"), which is the pointer to the arity-2 direct call. */
126+
110127#if defined(__GNUC__ ) && defined(__amd64__ )
111128static code_t rocq_accumulate_addr asm("rocq_accumulate_addr" ) __attribute__((used ));
112129
113130value rocq_proxy_accu (value clos ) {
114131 value v ;
115132 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. */
117133 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. */
123134 asm("jmp 1f\n\t"
124135 ".align 8\n\t"
125136 ".quad 3067\n"
126137 "2:\n\t"
127138 "mov %%rax, %%rdi\n\t"
128139 "mov 16(%%rbx), %%rax\n\t"
129140 "mov %%rdi, %%rbx\n\t"
130- "mov rocq_accumulate_addr(%%rip), %%rdi\n\t"
131- "jmp *%%rdi\n"
141+ "lea rocq_accumulate_addr(%%rip), %%rdi\n\t"
142+ "jmp *( %%rdi) \n"
132143 "1:\n\t"
133144 "lea 2b(%%rip), %0\n\t"
134145 : "=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. */
138146 value r = caml_alloc_small (1 , 0 );
139147 Field (r , 0 ) = v ;
140148 return r ;
141149}
150+
151+ #elif defined(__GNUC__ ) && defined(__i386__ )
152+ static code_t rocq_accumulate_addr asm("rocq_accumulate_addr" ) __attribute__((used ));
153+
154+ value rocq_proxy_accu (value clos ) {
155+ value v ;
156+ CAMLassert (Tag_val (clos ) == Closure_tag && Arity_closinfo (Closinfo_val (clos )) == 2 );
157+ rocq_accumulate_addr = ((code_t * )clos )[2 ];
158+ asm("jmp 1f\n\t"
159+ ".align 4\n\t"
160+ ".long 3067\n"
161+ "2:\n\t"
162+ "mov %%eax, %%ecx\n\t"
163+ "mov 8(%%ebx), %%eax\n\t"
164+ "mov %%ecx, %%ebx\n\t"
165+ "call __x86.get_pc_thunk.cx\n\t"
166+ "add $_GLOBAL_OFFSET_TABLE_, %%ecx\n\t"
167+ "mov rocq_accumulate_addr@GOT(%%ecx), %%ecx\n\t"
168+ "jmp *(%%ecx)\n"
169+ "1:\n\t"
170+ "call __x86.get_pc_thunk.ax\n\t"
171+ "add $_GLOBAL_OFFSET_TABLE_, %%eax\n\t"
172+ "lea 2b@GOTOFF(%%eax), %0\n\t"
173+ : "=r" (v ) : : "eax" );
174+ value r = caml_alloc_small (1 , 0 );
175+ Field (r , 0 ) = v ;
176+ return r ;
177+ }
178+
142179#else
143180value rocq_proxy_accu (value ) {
144181 return 1 ;
0 commit comments