Przeglądaj źródła

[cpp] Use if-goto instead of if-else for switch statements. Merges https://github.com/HaxeFoundation/haxe/pull/5079. Closes #4865

Hugh 9 lat temu
rodzic
commit
ca94cda3a8
1 zmienionych plików z 65 dodań i 40 usunięć
  1. 65 40
      src/generators/gencpp.ml

+ 65 - 40
src/generators/gencpp.ml

@@ -208,6 +208,7 @@ type context =
    (* Per file *)
    ctx_output : string -> unit;
    ctx_writer : source_writer;
+   ctx_label_id : int ref;
 
    ctx_interface_slot : (string,int) Hashtbl.t ref;
    ctx_interface_slot_count : int ref;
@@ -222,6 +223,7 @@ let result =
 {
    ctx_common = common_ctx;
    ctx_writer = null_file;
+   ctx_label_id = ref (-1);
    ctx_output = (null_file#write);
    ctx_interface_slot = ref (Hashtbl.create 0);
    ctx_interface_slot_count = ref 2;
@@ -237,6 +239,7 @@ let file_context ctx writer debug =
    { ctx with
       ctx_writer = writer;
       ctx_output = (writer#write);
+      ctx_label_id = ref (-1);
    }
 ;;
 
@@ -1125,20 +1128,6 @@ let array_arg_list inList =
 (* See if there is a haxe break statement that will be swollowed by c++ break *)
 exception BreakFound;;
 
-let contains_break expression =
-   try (
-   let rec check_all expression = match expression.eexpr with
-         | TBreak -> raise BreakFound
-         | TFor _
-         | TFunction _
-         | TWhile (_,_,_) -> ()
-         | _ -> Type.iter check_all expression;
-   in
-   check_all expression;
-   false;
-   ) with BreakFound -> true;;
-
-
 (* Decide is we should look the field up by name *)
 let dynamic_internal = function | "__Is" -> true | _ -> false
 
@@ -1460,13 +1449,14 @@ and tcpp_expr_expr =
    | CppBlock of tcppexpr list * tcpp_closure list
    | CppFor of tvar * tcppexpr * tcppexpr
    | CppIf of tcppexpr * tcppexpr * tcppexpr option
-   | CppWhile of tcppexpr * tcppexpr * Ast.while_flag
+   | CppWhile of tcppexpr * tcppexpr * Ast.while_flag * int
    | CppIntSwitch of tcppexpr * (Int32.t list * tcppexpr) list * tcppexpr option
-   | CppSwitch of tcppexpr * tcpp * (tcppexpr list * tcppexpr) list * tcppexpr option
+   | CppSwitch of tcppexpr * tcpp * (tcppexpr list * tcppexpr) list * tcppexpr option * int
    | CppTry of tcppexpr * (tvar * tcppexpr) list
    | CppBreak
    | CppContinue
    | CppClassOf of path * bool
+   | CppGoto of int
    | CppReturn of tcppexpr option
    | CppThrow of tcppexpr
    | CppEnumParameter of tcppexpr * tenum_field * int
@@ -1542,6 +1532,7 @@ let rec s_tcpp = function
    | CppBreak -> "CppBreak"
    | CppContinue -> "CppContinue"
    | CppClassOf _ -> "CppClassOf"
+   | CppGoto _ -> "CppGoto"
    | CppReturn _ -> "CppReturn"
    | CppThrow _ -> "CppThrow"
    | CppEnumParameter _ -> "CppEnumParameter"
@@ -2071,6 +2062,15 @@ let cpp_member_name_of member =
 ;;
 
 
+let cpp_append_block block expr =
+   match block.cppexpr with
+   | CppBlock(expr_list, closures) ->
+       { block with cppexpr = CppBlock( expr_list @ [expr], closures) }
+   | _ -> error "Internal error appending expression" block.cpppos
+;;
+
+
+
 let cpp_enum_name_of field =
    let rename = get_meta_string field.ef_meta Meta.Native in
    if rename <> "" then
@@ -2087,6 +2087,23 @@ let retype_expression ctx request_type function_args expression_tree forInjectio
    let uses_this = ref None in
    let injection = ref forInjection in
    let this_real = ref (if ctx.ctx_real_this_ptr then ThisReal else ThisDynamic) in
+   let label_counter = ctx.ctx_label_id in
+   let loop_stack = ref [] in
+   let alloc_label () =
+      incr label_counter;
+      !label_counter
+   in
+   let begin_loop () =
+      loop_stack := (alloc_label (),ref false) :: !loop_stack;
+      (fun () -> match !loop_stack with
+         | (label_id,used) :: tl ->
+            loop_stack := tl;
+            if !used then label_id else -1
+         | [] ->
+            error "Invalid inernal loop handling" expression_tree.epos
+      )
+   in
+
    (* '__trace' is at the top-level *)
    Hashtbl.add !declarations "__trace" ();
    List.iter (fun arg -> Hashtbl.add !declarations arg.v_name () ) function_args;
@@ -2108,6 +2125,7 @@ let retype_expression ctx request_type function_args expression_tree forInjectio
 
    let rec retype return_type expr =
       let cpp_type_of t = cpp_type_of ctx t in
+      let mk_cppexpr newExpr newType = { cppexpr = newExpr; cpptype = newType; cpppos = expr.epos } in
       let retypedExpr, retypedType =
          match expr.eexpr with
          | TEnumParameter( enumObj, enumField, enumIndex  ) ->
@@ -2143,7 +2161,13 @@ let retype_expression ctx request_type function_args expression_tree forInjectio
             end
 
          | TBreak ->
-            CppBreak, TCppVoid
+            begin match !loop_stack with
+               | [] ->
+                  CppBreak, TCppVoid
+               | (label_id,used) :: _ ->
+                  used := true;
+                  (CppGoto label_id),TCppVoid
+            end
 
          | TContinue ->
             CppContinue, TCppVoid
@@ -2510,8 +2534,9 @@ let retype_expression ctx request_type function_args expression_tree forInjectio
 
          | TWhile (e1,e2,flag) ->
             let condition = retype (TCppScalar("Bool")) e1 in
+            let close = begin_loop() in
             let block = retype TCppVoid (mk_block e2) in
-            CppWhile(condition, block, flag), TCppVoid
+            CppWhile(condition, block, flag, close()), TCppVoid
 
          | TArrayDecl el ->
             let retypedEls = List.map (retype TCppDynamic) el in
@@ -2580,16 +2605,18 @@ let retype_expression ctx request_type function_args expression_tree forInjectio
             let cppDef = match def with None -> None | Some e -> Some (retype TCppVoid (mk_block e)) in
             (try
                (match conditionType with TCppScalar("Int") | TCppScalar("Bool") -> () | _ -> raise Not_found );
-               (match def with None -> () | Some e -> if (contains_break e) then raise Not_found);
                let cases = List.map (fun (el,e2) ->
-                  if (contains_break e2) then raise Not_found;
                   (List.map const_int_of el), (retype TCppVoid (mk_block e2)) ) cases in
                CppIntSwitch(condition, cases, cppDef), TCppVoid
             with Not_found ->
+               let label = alloc_label () in
                (* do something better maybe ... *)
                let cases = List.map (fun (el,e2) ->
-                  (List.map (retype conditionType) el), (retype TCppVoid (mk_block e2)) ) cases in
-               CppSwitch(condition, conditionType, cases, cppDef), TCppVoid
+                  let cppBlock = retype TCppVoid (mk_block e2) in
+                  let gotoExpr = { cppexpr = CppGoto(label); cpptype = TCppVoid; cpppos = e2.epos } in
+                  let cppBlock = cpp_append_block cppBlock  gotoExpr in
+                  (List.map (retype conditionType) el), cppBlock ) cases in
+               CppSwitch(condition, conditionType, cases, cppDef, label), TCppVoid
             )
 
          | TTry (try_block,catches) ->
@@ -2640,7 +2667,6 @@ let retype_expression ctx request_type function_args expression_tree forInjectio
                CppTCast(baseCpp, return_type), return_type
             )
       in
-      let mk_cppexpr newExpr newType = { cppexpr = newExpr; cpptype = newType; cpppos = expr.epos } in
       let cppExpr = mk_cppexpr retypedExpr retypedType in
 
       (* Auto cast rules... *)
@@ -2744,7 +2770,6 @@ let gen_type ctx haxe_type =
 ;;
 
 
-
 let gen_cpp_ast_expression_tree ctx class_name func_name function_args injection tree =
    let writer = ctx.ctx_writer in
    let out = ctx.ctx_output in
@@ -2767,6 +2792,7 @@ let gen_cpp_ast_expression_tree ctx class_name func_name function_args injection
 
    let forInjection = match injection with Some inject -> inject.inj_setvar<>"" | _ -> false in
    let cppTree =  retype_expression ctx TCppVoid function_args tree forInjection in
+   let label_name i = Printf.sprintf "_hx_goto_%i" i in
 
    let rec gen_with_injection injection expr =
       (match expr.cppexpr with
@@ -2810,6 +2836,7 @@ let gen_cpp_ast_expression_tree ctx class_name func_name function_args injection
 
       | CppBreak -> out "break"
       | CppContinue -> out "continue"
+      | CppGoto label -> out ("goto " ^ (label_name label));
 
       | CppVarDecl(var,init) ->
          let name =  cpp_var_name_of var in
@@ -3180,31 +3207,28 @@ let gen_cpp_ast_expression_tree ctx class_name func_name function_args injection
          | Some expr -> output_i "default:"; gen expr; | _ -> ()  );
          out spacer;
          writer#end_block;
-      | CppSwitch(condition, conditionType, cases, optional_default) ->
+      | CppSwitch(condition, conditionType, cases, optional_default, label) ->
          let tmp_name = "_hx_switch_" ^ (string_of_int !tempId) in
          incr tempId;
          out ( (tcpp_to_string conditionType) ^ " " ^ tmp_name ^ " = " );
          gen condition;
          out ";\n";
-         let else_str = ref "" in
-         if (List.length cases > 0) then
-            List.iter (fun (cases,expression) ->
-               output_i ( !else_str ^ "if ( ");
-               else_str := "else ";
-               let or_str = ref "" in
-               List.iter (fun value ->
-                  out (!or_str ^ " (" ^ tmp_name ^ "=="); gen value; out ")";
-                  or_str := " || ";
-                  ) cases;
-               out (" )");
-               gen expression;
+         List.iter (fun (cases,expression) ->
+            output_i "if ( ";
+            let or_str = ref "" in
+            List.iter (fun value ->
+               out (!or_str ^ " (" ^ tmp_name ^ "=="); gen value; out ")";
+               or_str := " || ";
                ) cases;
+            out (" )");
+            gen expression;
+            ) cases;
          (match optional_default with | None -> ()
          | Some default ->
-            out spacer;
-            output_i ( !else_str ^ " ");
+            output_i "/* default */";
             gen default;
          );
+         output_i ((label_name label) ^ ":")
 
       | CppUnop(unop,value) ->
            out (match unop with
@@ -3214,7 +3238,7 @@ let gen_cpp_ast_expression_tree ctx class_name func_name function_args injection
            );
            out "(";  gen value; out ")"
 
-      | CppWhile(condition, block, while_flag) ->
+      | CppWhile(condition, block, while_flag, loop_id) ->
           (match while_flag with
           | NormalWhile ->
               out "while("; gen condition; out (")");
@@ -3224,6 +3248,7 @@ let gen_cpp_ast_expression_tree ctx class_name func_name function_args injection
               gen block;
               out "while("; gen condition; out ")"
           );
+          if loop_id > -1 then output_i ((label_name loop_id) ^ ":");
 
       | CppIf (condition,block,None) ->
           out "if ("; gen condition; out (") ");