Skip to content

Commit cb8c355

Browse files
mshinwellgretay-js
authored andcommitted
Mark Obj_dup as not having effects and handle in Cfg (ocaml-flambda#3766)
1 parent f7586bc commit cb8c355

File tree

5 files changed

+55
-13
lines changed

5 files changed

+55
-13
lines changed

backend/cfg/cfg.ml

+9-1
Original file line numberDiff line numberDiff line change
@@ -412,7 +412,13 @@ let can_raise_terminator (i : terminator) =
412412
| Raise _ | Tailcall_func _ | Call _ | Prim { op = Probe _; label_after = _ }
413413
->
414414
true
415-
| Prim { op = External { alloc; _ }; label_after = _ } -> alloc
415+
| Prim { op = External { alloc; effects; _ }; label_after = _ } -> (
416+
if not alloc
417+
then false
418+
else
419+
(* Even if going via [caml_c_call], if there are no effects, the function
420+
cannot raise an exception. (Example: [caml_obj_dup].) *)
421+
match effects with No_effects -> false | Arbitrary_effects -> true)
416422
| Specific_can_raise { op; _ } ->
417423
assert (Arch.operation_can_raise op);
418424
true
@@ -423,6 +429,8 @@ let can_raise_terminator (i : terminator) =
423429
(* CR gyorsh: [is_pure_terminator] is not the same as [can_raise_terminator]
424430
because of [Tailcal Self] which is not pure but marked as cannot raise at the
425431
moment, which we might want to reconsider later. *)
432+
(* CR mshinwell/gyorsh: maybe this function could be made more precise e.g.
433+
taking into account [effects] on extcalls *)
426434
let is_pure_terminator desc =
427435
match (desc : terminator) with
428436
| Return | Raise _ | Call_no_return _ | Tailcall_func _ | Tailcall_self _

middle_end/flambda2/to_cmm/to_cmm_primitive.ml

+2-2
Original file line numberDiff line numberDiff line change
@@ -858,8 +858,8 @@ let unary_primitive env res dbg f arg =
858858
( None,
859859
res,
860860
C.extcall ~dbg ~alloc:true ~returns:true ~is_c_builtin:false
861-
~effects:Arbitrary_effects ~coeffects:Has_coeffects ~ty_args:[]
862-
"caml_obj_dup" Cmm.typ_val [arg] )
861+
~effects:No_effects ~coeffects:Has_coeffects ~ty_args:[] "caml_obj_dup"
862+
Cmm.typ_val [arg] )
863863
| Is_int _ -> None, res, C.and_int arg (C.int ~dbg 1) dbg
864864
| Is_null -> None, res, C.eq ~dbg arg (C.nativeint ~dbg 0n)
865865
| Get_tag -> None, res, C.get_tag arg dbg

runtime/obj.c

+12-5
Original file line numberDiff line numberDiff line change
@@ -154,15 +154,22 @@ CAMLprim value caml_obj_with_tag(value new_tag_v, value arg)
154154
tag_t tag_for_alloc;
155155
uintnat infix_offset = 0;
156156

157+
// This function must not raise exceptions (except asynchronous exceptions).
158+
// It behaves just like an allocation function, which has generative effects,
159+
// but not arbitrary effects. (See the [Obj_dup] case in [To_cmm].)
160+
157161
tag_t new_tag = (tag_t)Long_val(new_tag_v);
162+
#ifdef DEBUG
158163
tag_t existing_tag = Tag_val(arg);
164+
#endif
159165

160-
if ((existing_tag == Closure_tag || existing_tag == Infix_tag
166+
CAMLassert (
167+
!(
168+
(existing_tag == Closure_tag || existing_tag == Infix_tag
161169
|| new_tag == Closure_tag || new_tag == Infix_tag)
162-
&& existing_tag != new_tag) {
163-
caml_failwith("Cannot change tags of existing closures or create \
164-
new closures using [caml_obj_with_tag]");
165-
}
170+
&& existing_tag != new_tag));
171+
// Cannot change tags of existing closures or create new closures using
172+
// [caml_obj_with_tag].
166173

167174
if (new_tag == Infix_tag) {
168175
// If we received an infix block, we must return the same; but the whole

runtime4/obj.c

+12-5
Original file line numberDiff line numberDiff line change
@@ -159,15 +159,22 @@ CAMLprim value caml_obj_with_tag(value new_tag_v, value arg)
159159
tag_t tag_for_alloc;
160160
uintnat infix_offset = 0;
161161

162+
// This function must not raise exceptions (except asynchronous exceptions).
163+
// It behaves just like an allocation function, which has generative effects,
164+
// but not arbitrary effects. (See the [Obj_dup] case in [To_cmm].)
165+
162166
tag_t new_tag = (tag_t)Long_val(new_tag_v);
167+
#ifdef DEBUG
163168
tag_t existing_tag = Tag_val(arg);
169+
#endif
164170

165-
if ((existing_tag == Closure_tag || existing_tag == Infix_tag
171+
CAMLassert (
172+
!(
173+
(existing_tag == Closure_tag || existing_tag == Infix_tag
166174
|| new_tag == Closure_tag || new_tag == Infix_tag)
167-
&& existing_tag != new_tag) {
168-
caml_failwith("Cannot change tags of existing closures or create \
169-
new closures using [caml_obj_with_tag]");
170-
}
175+
&& existing_tag != new_tag));
176+
// Cannot change tags of existing closures or create new closures using
177+
// [caml_obj_with_tag].
171178

172179
if (new_tag == Infix_tag) {
173180
// If we received an infix block, we must return the same; but the whole
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
(* TEST *)
2+
3+
[@@@ocaml.flambda_o3]
4+
5+
let[@inline never] bar _ _ = ()
6+
7+
let[@inline never] baz () = ()
8+
9+
let foo size (x : int) arr y =
10+
let extra_arg_array = ref arr in
11+
let extra_arg_int = ref 0 in
12+
(try
13+
for i = 0 to size - 1 do
14+
if i > 20 then (
15+
let _ = Sys.opaque_identity (Obj.dup y) in
16+
baz ()
17+
)
18+
done
19+
with _exn -> ());
20+
bar !extra_arg_array !extra_arg_int

0 commit comments

Comments
 (0)