aboutsummaryrefslogtreecommitdiffstats
path: root/erts/emulator
diff options
context:
space:
mode:
authorSverker Eriksson <[email protected]>2016-07-05 19:09:16 +0200
committerSverker Eriksson <[email protected]>2016-07-05 19:09:16 +0200
commita94f909586a7b69ba2f4539f4c40e3600cf705d2 (patch)
treedbc999e7120a41fe7acb41f6d1b4afb001318cc3 /erts/emulator
parent04bb724a572c78d775df2ec12a1694870369c3b4 (diff)
parent520654d49d9f1c1548dc2a5800c7330001cd3064 (diff)
downloadotp-a94f909586a7b69ba2f4539f4c40e3600cf705d2.tar.gz
otp-a94f909586a7b69ba2f4539f4c40e3600cf705d2.tar.bz2
otp-a94f909586a7b69ba2f4539f4c40e3600cf705d2.zip
Merge branch 'hipe-trap-gc/PR-1116/OTP-13724' into maint
* hipe-trap-gc/PR-1116: hipe: Fix bug in trap frame allocation wrappers hipe: Add assertion for gc in bif without wrapper
Diffstat (limited to 'erts/emulator')
-rw-r--r--erts/emulator/hipe/hipe_amd64_asm.m422
-rw-r--r--erts/emulator/hipe/hipe_amd64_bifs.m43
-rw-r--r--erts/emulator/hipe/hipe_amd64_glue.S2
-rw-r--r--erts/emulator/hipe/hipe_arm_asm.m432
-rw-r--r--erts/emulator/hipe/hipe_arm_bifs.m433
-rw-r--r--erts/emulator/hipe/hipe_arm_glue.S4
-rw-r--r--erts/emulator/hipe/hipe_bif_list.m447
-rw-r--r--erts/emulator/hipe/hipe_gc.c4
-rw-r--r--erts/emulator/hipe/hipe_mkliterals.c6
-rw-r--r--erts/emulator/hipe/hipe_ppc_bifs.m436
-rw-r--r--erts/emulator/hipe/hipe_process.h6
-rw-r--r--erts/emulator/hipe/hipe_sparc_bifs.m434
-rw-r--r--erts/emulator/hipe/hipe_x86_bifs.m43
13 files changed, 198 insertions, 34 deletions
diff --git a/erts/emulator/hipe/hipe_amd64_asm.m4 b/erts/emulator/hipe/hipe_amd64_asm.m4
index 2c0fbbee2d..409fd0ef89 100644
--- a/erts/emulator/hipe/hipe_amd64_asm.m4
+++ b/erts/emulator/hipe/hipe_amd64_asm.m4
@@ -121,6 +121,22 @@ define(NSP,%rsp)dnl
/*
+ * Debugging macros
+ *
+ * Keeps track of whether context has been saved in the debug build, allowing us
+ * to detect when the garbage collector is called when it shouldn't.
+ */
+`#ifdef DEBUG
+# define SET_GC_UNSAFE \
+ movq $1, P_GCUNSAFE(P)
+# define SET_GC_SAFE \
+ movq $0, P_GCUNSAFE(P)
+#else
+# define SET_GC_UNSAFE
+# define SET_GC_SAFE
+#endif'
+
+/*
* Context switching macros.
*/
`#define SWITCH_C_TO_ERLANG_QUICK \
@@ -133,12 +149,14 @@ define(NSP,%rsp)dnl
`#define SAVE_CACHED_STATE \
SAVE_HP; \
- SAVE_FCALLS'
+ SAVE_FCALLS; \
+ SET_GC_SAFE'
`#define RESTORE_CACHED_STATE \
RESTORE_HP; \
RESTORE_HEAP_LIMIT; \
- RESTORE_FCALLS'
+ RESTORE_FCALLS; \
+ SET_GC_UNSAFE'
`#define SWITCH_C_TO_ERLANG \
RESTORE_CACHED_STATE; \
diff --git a/erts/emulator/hipe/hipe_amd64_bifs.m4 b/erts/emulator/hipe/hipe_amd64_bifs.m4
index 9cf3bf74fd..21739726bb 100644
--- a/erts/emulator/hipe/hipe_amd64_bifs.m4
+++ b/erts/emulator/hipe/hipe_amd64_bifs.m4
@@ -600,10 +600,11 @@ noproc_primop_interface_0(nbif_handle_fp_exception, erts_restore_fpu)
define(gc_bif_interface_0,`nofail_primop_interface_0($1, $2)')
/*
- * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2).
+ * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2,3).
*/
define(gc_bif_interface_1,`standard_bif_interface_1($1, $2)')
define(gc_bif_interface_2,`standard_bif_interface_2($1, $2)')
+define(gc_bif_interface_3,`standard_bif_interface_3($1, $2)')
/*
* Implement gc_nofail_primop_interface_1 as nofail_primop_interface_1.
diff --git a/erts/emulator/hipe/hipe_amd64_glue.S b/erts/emulator/hipe/hipe_amd64_glue.S
index b37ed3c68a..f3404888d5 100644
--- a/erts/emulator/hipe/hipe_amd64_glue.S
+++ b/erts/emulator/hipe/hipe_amd64_glue.S
@@ -94,6 +94,7 @@ ASYM(nbif_return):
.nosave_exit:
/* switch to C stack */
SWITCH_ERLANG_TO_C_QUICK
+ SET_GC_SAFE
/* restore C callee-save registers, drop frame, return */
movq (%rsp), %rbp # kills P
movq 8(%rsp), %rbx
@@ -398,6 +399,7 @@ nbif_4_simple_exception:
movl %eax, P_NARITY(P) # Note: narity is a 32-bit field
/* find and prepare to invoke the handler */
SWITCH_ERLANG_TO_C_QUICK # The cached state is clean and need not be saved.
+ SET_GC_SAFE
movq P, %rdi
call CSYM(hipe_handle_exception) # Note: hipe_handle_exception() conses
SWITCH_C_TO_ERLANG # %rsp updated by hipe_find_handler()
diff --git a/erts/emulator/hipe/hipe_arm_asm.m4 b/erts/emulator/hipe/hipe_arm_asm.m4
index ae9ec752bb..68a6faa70b 100644
--- a/erts/emulator/hipe/hipe_arm_asm.m4
+++ b/erts/emulator/hipe/hipe_arm_asm.m4
@@ -48,6 +48,24 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive
`#define TEMP_LR r8'
/*
+ * Debugging macros
+ *
+ * Keeps track of whether context has been saved in the debug build, allowing us
+ * to detect when the garbage collector is called when it shouldn't.
+ */
+`#ifdef DEBUG
+# define SET_GC_UNSAFE(SCRATCH) \
+ mov SCRATCH, #1; \
+ str SCRATCH, [P, #P_GCUNSAFE]
+# define SET_GC_SAFE(SCRATCH) \
+ mov SCRATCH, #0; \
+ str SCRATCH, [P, #P_GCUNSAFE]
+#else
+# define SET_GC_UNSAFE(SCRATCH)
+# define SET_GC_SAFE(SCRATCH)
+#endif'
+
+/*
* Context switching macros.
*
* RESTORE_CONTEXT and RESTORE_CONTEXT_QUICK do not affect
@@ -59,12 +77,14 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive
`#define RESTORE_CONTEXT_QUICK \
mov lr, TEMP_LR'
-`#define SAVE_CACHED_STATE \
- str HP, [P, #P_HP]; \
- str NSP, [P, #P_NSP]'
+`#define SAVE_CACHED_STATE \
+ str HP, [P, #P_HP]; \
+ str NSP, [P, #P_NSP]; \
+ SET_GC_SAFE(HP)'
-`#define RESTORE_CACHED_STATE \
- ldr HP, [P, #P_HP]; \
+`#define RESTORE_CACHED_STATE \
+ SET_GC_UNSAFE(HP); \
+ ldr HP, [P, #P_HP]; \
ldr NSP, [P, #P_NSP]'
`#define SAVE_CONTEXT_BIF \
@@ -75,12 +95,14 @@ define(NR_ARG_REGS,3)dnl admissible values are 0 to 6, inclusive
ldr HP, [P, #P_HP]'
`#define SAVE_CONTEXT_GC \
+ SET_GC_SAFE(TEMP_LR); \
mov TEMP_LR, lr; \
str lr, [P, #P_NRA]; \
str NSP, [P, #P_NSP]; \
str HP, [P, #P_HP]'
`#define RESTORE_CONTEXT_GC \
+ SET_GC_UNSAFE(HP); \
ldr HP, [P, #P_HP]'
/*
diff --git a/erts/emulator/hipe/hipe_arm_bifs.m4 b/erts/emulator/hipe/hipe_arm_bifs.m4
index d9c9952dbf..d7a2fec04a 100644
--- a/erts/emulator/hipe/hipe_arm_bifs.m4
+++ b/erts/emulator/hipe/hipe_arm_bifs.m4
@@ -198,8 +198,9 @@ $1:
* gc_bif_interface_0(nbif_name, cbif_name)
* gc_bif_interface_1(nbif_name, cbif_name)
* gc_bif_interface_2(nbif_name, cbif_name)
+ * gc_bif_interface_3(nbif_name, cbif_name)
*
- * Generate native interface for a BIF with 0-2 parameters and
+ * Generate native interface for a BIF with 0-3 parameters and
* standard failure mode.
* The BIF may do a GC.
*/
@@ -279,6 +280,36 @@ $1:
.type $1, %function
#endif')
+define(gc_bif_interface_3,
+`
+#ifndef HAVE_$1
+#`define' HAVE_$1
+ .global $1
+$1:
+ /* Set up C argument registers. */
+ mov r0, P
+ NBIF_ARG(r1,3,0)
+ NBIF_ARG(r2,3,1)
+ NBIF_ARG(r3,3,2)
+
+ /* Save caller-save registers and call the C function. */
+ SAVE_CONTEXT_GC
+ str r1, [r0, #P_ARG0] /* Store BIF__ARGS in def_arg_reg[] */
+ str r2, [r0, #P_ARG1]
+ str r3, [r0, #P_ARG2]
+ add r1, r0, #P_ARG0
+ CALL_BIF($2)
+ TEST_GOT_MBUF(3)
+
+ /* Restore registers. Check for exception. */
+ cmp r0, #THE_NON_VALUE
+ RESTORE_CONTEXT_GC
+ beq nbif_3_simple_exception
+ NBIF_RET(3)
+ .size $1, .-$1
+ .type $1, %function
+#endif')
+
/*
* gc_nofail_primop_interface_1(nbif_name, cbif_name)
*
diff --git a/erts/emulator/hipe/hipe_arm_glue.S b/erts/emulator/hipe/hipe_arm_glue.S
index 49ffa8b1d8..5b7f8ad52d 100644
--- a/erts/emulator/hipe/hipe_arm_glue.S
+++ b/erts/emulator/hipe/hipe_arm_glue.S
@@ -342,6 +342,7 @@ nbif_4_gc_after_bif:
str r1, [P, #P_NARITY]
str TEMP_LR, [P, #P_NRA]
str NSP, [P, #P_NSP]
+ SET_GC_SAFE(TEMP_LR)
mov TEMP_LR, lr
mov r3, #0 /* Pass 0 in arity */
mov r2, #0 /* Pass NULL in regs */
@@ -349,6 +350,7 @@ nbif_4_gc_after_bif:
mov r0, P
bl erts_gc_after_bif_call
mov lr, TEMP_LR
+ SET_GC_UNSAFE(TEMP_LR)
ldr TEMP_LR, [P, #P_NRA]
mov r1, #0
str r1, [P, #P_NARITY]
@@ -404,6 +406,7 @@ nbif_4_simple_exception:
str NSP, [P, #P_NSP]
str TEMP_LR, [P, #P_NRA]
str r1, [P, #P_NARITY]
+ SET_GC_SAFE(r0)
/* find and prepare to invoke the handler */
mov r0, P
bl hipe_handle_exception /* Note: hipe_handle_exception() conses */
@@ -423,6 +426,7 @@ nbif_4_simple_exception:
str NSP, [P, #P_NSP]
str r1, [P, #P_NARITY]
str TEMP_LR, [P, #P_NRA]
+ SET_GC_SAFE(NSP)
b .nosave_exit
/*
diff --git a/erts/emulator/hipe/hipe_bif_list.m4 b/erts/emulator/hipe/hipe_bif_list.m4
index 29095a5389..dcf3447af9 100644
--- a/erts/emulator/hipe/hipe_bif_list.m4
+++ b/erts/emulator/hipe/hipe_bif_list.m4
@@ -96,6 +96,7 @@
* gc_bif_interface_0(nbif_name, cbif_name)
* gc_bif_interface_1(nbif_name, cbif_name)
* gc_bif_interface_2(nbif_name, cbif_name)
+ * gc_bif_interface_3(nbif_name, cbif_name)
*
* A BIF which may do a GC or walk the native stack.
* May read NSP, NSP_LIMIT, NRA, HP, HP_LIMIT, and FCALLS.
@@ -263,32 +264,34 @@ noproc_primop_interface_1(nbif_atomic_inc, hipe_atomic_inc)
',)dnl
/*
- * Standard BIFs.
- * BIF_LIST(ModuleAtom,FunctionAtom,Arity,CFun,Index)
+ * BIFs that disable GC while trapping are called via a wrapper
+ * to reserve stack space for the "trap frame".
+ * They occasionally need to call the garbage collector in order to make room
+ * for the trap frame on the BEAM stack.
*/
+gc_bif_interface_1(nbif_term_to_binary_1, hipe_wrapper_term_to_binary_1)
+gc_bif_interface_2(nbif_term_to_binary_2, hipe_wrapper_term_to_binary_2)
+gc_bif_interface_1(nbif_binary_to_term_1, hipe_wrapper_binary_to_term_1)
+gc_bif_interface_2(nbif_binary_to_term_2, hipe_wrapper_binary_to_term_2)
+gc_bif_interface_1(nbif_binary_to_list_1, hipe_wrapper_binary_to_list_1)
+gc_bif_interface_3(nbif_binary_to_list_3, hipe_wrapper_binary_to_list_3)
+gc_bif_interface_1(nbif_bitstring_to_list_1, hipe_wrapper_bitstring_to_list_1)
+gc_bif_interface_1(nbif_list_to_binary_1, hipe_wrapper_list_to_binary_1)
+gc_bif_interface_1(nbif_iolist_to_binary_1, hipe_wrapper_iolist_to_binary_1)
+gc_bif_interface_1(nbif_binary_list_to_bin_1, hipe_wrapper_binary_list_to_bin_1)
+gc_bif_interface_1(nbif_list_to_bitstring_1, hipe_wrapper_list_to_bitstring_1)
+gc_bif_interface_2(nbif_send_2, hipe_wrapper_send_2)
+gc_bif_interface_3(nbif_send_3, hipe_wrapper_send_3)
+gc_bif_interface_2(nbif_ebif_bang_2, hipe_wrapper_ebif_bang_2)
+gc_bif_interface_2(nbif_maps_merge_2, hipe_wrapper_maps_merge_2)
-/* BIFs that disable GC while trapping are called via a wrapper
- * to reserve stack space for the "trap frame".
+
+/*
+ * Standard BIFs.
+ * BIF_LIST(ModuleAtom,FunctionAtom,Arity,CFun,Index)
*/
-define(CFUN,`ifelse(
-$1, term_to_binary_1, hipe_wrapper_$1,
-$1, term_to_binary_2, hipe_wrapper_$1,
-$1, binary_to_term_1, hipe_wrapper_$1,
-$1, binary_to_term_2, hipe_wrapper_$1,
-$1, binary_to_list_1, hipe_wrapper_$1,
-$1, binary_to_list_3, hipe_wrapper_$1,
-$1, bitstring_to_list_1, hipe_wrapper_$1,
-$1, list_to_binary_1, hipe_wrapper_$1,
-$1, iolist_to_binary_1, hipe_wrapper_$1,
-$1, binary_list_to_bin_1, hipe_wrapper_$1,
-$1, list_to_bitstring_1, hipe_wrapper_$1,
-$1, send_2, hipe_wrapper_$1,
-$1, send_3, hipe_wrapper_$1,
-$1, ebif_bang_2, hipe_wrapper_$1,
-$1, maps_merge_2, hipe_wrapper_$1,
-$1)')
-define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, CFUN($4))')
+define(BIF_LIST,`standard_bif_interface_$3(nbif_$4, $4)')
include(TARGET/`erl_bif_list.h')
/*
diff --git a/erts/emulator/hipe/hipe_gc.c b/erts/emulator/hipe/hipe_gc.c
index d0619a0609..566c65882e 100644
--- a/erts/emulator/hipe/hipe_gc.c
+++ b/erts/emulator/hipe/hipe_gc.c
@@ -46,6 +46,8 @@ Eterm *fullsweep_nstack(Process *p, Eterm *n_htop)
/* arch-specific nstack walk state */
struct nstack_walk_state walk_state;
+ ASSERT(!p->hipe.gc_is_unsafe);
+
if (!p->hipe.nstack) {
ASSERT(!p->hipe.nsp && !p->hipe.nstend);
return n_htop;
@@ -136,6 +138,8 @@ void gensweep_nstack(Process *p, Eterm **ptr_old_htop, Eterm **ptr_n_htop)
char *mature;
Uint mature_size;
+ ASSERT(!p->hipe.gc_is_unsafe);
+
if (!p->hipe.nstack) {
ASSERT(!p->hipe.nsp && !p->hipe.nstend);
return;
diff --git a/erts/emulator/hipe/hipe_mkliterals.c b/erts/emulator/hipe/hipe_mkliterals.c
index 0d3493ec6c..b9d4226705 100644
--- a/erts/emulator/hipe/hipe_mkliterals.c
+++ b/erts/emulator/hipe/hipe_mkliterals.c
@@ -525,6 +525,12 @@ static const struct rts_param rts_params[] = {
{ 51, "P_CALLEE_EXP", 1, offsetof(struct process, hipe.u.callee_exp) },
{ 52, "THE_NON_VALUE", 1, (int)THE_NON_VALUE },
+
+ { 53, "P_GCUNSAFE",
+#ifdef DEBUG
+ 1, offsetof(struct process, hipe.gc_is_unsafe)
+#endif
+ },
};
#define NR_PARAMS ARRAY_SIZE(rts_params)
diff --git a/erts/emulator/hipe/hipe_ppc_bifs.m4 b/erts/emulator/hipe/hipe_ppc_bifs.m4
index 57b4208bee..b540562185 100644
--- a/erts/emulator/hipe/hipe_ppc_bifs.m4
+++ b/erts/emulator/hipe/hipe_ppc_bifs.m4
@@ -212,8 +212,9 @@ ASYM($1):
* gc_bif_interface_0(nbif_name, cbif_name)
* gc_bif_interface_1(nbif_name, cbif_name)
* gc_bif_interface_2(nbif_name, cbif_name)
+ * gc_bif_interface_3(nbif_name, cbif_name)
*
- * Generate native interface for a BIF with 0-2 parameters and
+ * Generate native interface for a BIF with 0-3 parameters and
* standard failure mode.
* The BIF may do a GC.
*/
@@ -300,6 +301,39 @@ ASYM($1):
TYPE_FUNCTION(ASYM($1))
#endif')
+define(gc_bif_interface_3,
+`
+#ifndef HAVE_$1
+#`define' HAVE_$1
+ GLOBAL(ASYM($1))
+ASYM($1):
+ /* Set up C argument registers. */
+ mr r3, P
+ NBIF_ARG(r4,3,0)
+ NBIF_ARG(r5,3,1)
+ NBIF_ARG(r6,3,2)
+
+ /* Save caller-save registers and call the C function. */
+ SAVE_CONTEXT_GC
+ STORE r4, P_ARG0(r3) /* Store BIF__ARGS in def_arg_reg[] */
+ STORE r5, P_ARG1(r3)
+ STORE r6, P_ARG2(r3)
+ addi r4, r3, P_ARG0
+ CALL_BIF($2)
+ TEST_GOT_MBUF
+
+ /* Restore registers. Check for exception. */
+ CMPI r3, THE_NON_VALUE
+ RESTORE_CONTEXT_GC
+ beq- 1f
+ NBIF_RET(3)
+1: /* workaround for bc:s small offset operand */
+ b CSYM(nbif_3_simple_exception)
+ HANDLE_GOT_MBUF(3)
+ SET_SIZE(ASYM($1))
+ TYPE_FUNCTION(ASYM($1))
+#endif')
+
/*
* gc_nofail_primop_interface_1(nbif_name, cbif_name)
*
diff --git a/erts/emulator/hipe/hipe_process.h b/erts/emulator/hipe/hipe_process.h
index 21c4239753..a8d5972280 100644
--- a/erts/emulator/hipe/hipe_process.h
+++ b/erts/emulator/hipe/hipe_process.h
@@ -52,6 +52,9 @@ struct hipe_process_state {
#if defined(ERTS_ENABLE_LOCK_CHECK) && defined(ERTS_SMP)
void (*bif_callee)(void); /* When calling BIF's via debug wrapper */
#endif
+#ifdef DEBUG
+ UWord gc_is_unsafe; /* Nonzero when GC-required state is on stack */
+#endif
};
extern void hipe_arch_print_pcb(struct hipe_process_state *p);
@@ -68,6 +71,9 @@ static __inline__ void hipe_init_process(struct hipe_process_state *p)
p->nra = NULL;
#endif
p->narity = 0;
+#ifdef DEBUG
+ p->gc_is_unsafe = 0;
+#endif
}
static __inline__ void hipe_delete_process(struct hipe_process_state *p)
diff --git a/erts/emulator/hipe/hipe_sparc_bifs.m4 b/erts/emulator/hipe/hipe_sparc_bifs.m4
index 2e886ec1d1..1389beaa61 100644
--- a/erts/emulator/hipe/hipe_sparc_bifs.m4
+++ b/erts/emulator/hipe/hipe_sparc_bifs.m4
@@ -210,8 +210,9 @@ $1:
* gc_bif_interface_0(nbif_name, cbif_name)
* gc_bif_interface_1(nbif_name, cbif_name)
* gc_bif_interface_2(nbif_name, cbif_name)
+ * gc_bif_interface_3(nbif_name, cbif_name)
*
- * Generate native interface for a BIF with 0-2 parameters and
+ * Generate native interface for a BIF with 0-3 parameters and
* standard failure mode.
* The BIF may do a GC.
*/
@@ -295,6 +296,37 @@ $1:
.type $1, #function
#endif')
+define(gc_bif_interface_3,
+`
+#ifndef HAVE_$1
+#`define' HAVE_$1
+ .global $1
+$1:
+ /* Set up C argument registers. */
+ mov P, %o0
+ NBIF_ARG(%o1,3,0)
+ NBIF_ARG(%o2,3,1)
+ NBIF_ARG(%o3,3,2)
+
+ /* Save caller-save registers and call the C function. */
+ SAVE_CONTEXT_GC
+ st %o1, [%o0+P_ARG0] ! Store BIF__ARGS in def_arg_reg
+ st %o2, [%o0+P_ARG1]
+ st %o3, [%o0+P_ARG2]
+ add %o0, P_ARG0, %o1
+ CALL_BIF($2)
+ nop
+ TEST_GOT_MBUF
+
+ /* Restore registers. Check for exception. */
+ TEST_GOT_EXN(3)
+ RESTORE_CONTEXT_GC
+ NBIF_RET(3)
+ HANDLE_GOT_MBUF(3)
+ .size $1, .-$1
+ .type $1, #function
+#endif')
+
/*
* gc_nofail_primop_interface_1(nbif_name, cbif_name)
*
diff --git a/erts/emulator/hipe/hipe_x86_bifs.m4 b/erts/emulator/hipe/hipe_x86_bifs.m4
index b8ac5046d5..c0c149733c 100644
--- a/erts/emulator/hipe/hipe_x86_bifs.m4
+++ b/erts/emulator/hipe/hipe_x86_bifs.m4
@@ -671,10 +671,11 @@ noproc_primop_interface_0(nbif_handle_fp_exception, erts_restore_fpu)
define(gc_bif_interface_0,`nofail_primop_interface_0($1, $2)')
/*
- * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2).
+ * Implement gc_bif_interface_N as standard_bif_interface_N (N=1,2,3).
*/
define(gc_bif_interface_1,`standard_bif_interface_1($1, $2)')
define(gc_bif_interface_2,`standard_bif_interface_2($1, $2)')
+define(gc_bif_interface_3,`standard_bif_interface_3($1, $2)')
/*
* Implement gc_nofail_primop_interface_1 as nofail_primop_interface_1.