Skip to content

Commit 7128a1e

Browse files
committed
Add support for native_compute on x86-32.
1 parent 77dcfbd commit 7128a1e

1 file changed

Lines changed: 48 additions & 11 deletions

File tree

kernel/byterun/rocq_values.c

Lines changed: 48 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -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__)
111128
static code_t rocq_accumulate_addr asm("rocq_accumulate_addr") __attribute__((used));
112129

113130
value 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
143180
value rocq_proxy_accu(value) {
144181
return 1;

0 commit comments

Comments
 (0)