{"action":"opened","issue":{"url":"https://api.github.com/repos/ocamllabs/ocaml-ctypes/issues/267","labels_url":"https://api.github.com/repos/ocamllabs/ocaml-ctypes/issues/267/labels{/name}","comments_url":"https://api.github.com/repos/ocamllabs/ocaml-ctypes/issues/267/comments","events_url":"https://api.github.com/repos/ocamllabs/ocaml-ctypes/issues/267/events","html_url":"https://github.com/ocamllabs/ocaml-ctypes/issues/267","id":56212995,"number":267,"title":"Switch pattern matching order in generated code","user":{"login":"yallop","id":1736496,"avatar_url":"https://avatars.githubusercontent.com/u/1736496?v=3","gravatar_id":"","url":"https://api.github.com/users/yallop","html_url":"https://github.com/yallop","followers_url":"https://api.github.com/users/yallop/followers","following_url":"https://api.github.com/users/yallop/following{/other_user}","gists_url":"https://api.github.com/users/yallop/gists{/gist_id}","starred_url":"https://api.github.com/users/yallop/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/yallop/subscriptions","organizations_url":"https://api.github.com/users/yallop/orgs","repos_url":"https://api.github.com/users/yallop/repos","events_url":"https://api.github.com/users/yallop/events{/privacy}","received_events_url":"https://api.github.com/users/yallop/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":0,"created_at":"2015-02-02T09:25:51Z","updated_at":"2015-02-02T09:25:51Z","closed_at":null,"body":"Cstubs currently generates code that looks like this\r\n\r\n```OCaml\r\nlet foreign : type a b. string -> (a -> b) Ctypes.fn -> (a -> b) =\r\n  fun name t -> match name, t with\r\n| \"retrieve_INT32_MAX\",\r\n  CI.Function (CI.Void, CI.Returns (CI.Primitive CI.Int32_t)) ->\r\n  cstubs_tests_23_retrieve_INT32_MAX\r\n| \"retrieve_INT32_MIN\",\r\n  CI.Function (CI.Void, CI.Returns (CI.Primitive CI.Int32_t)) ->\r\n  cstubs_tests_22_retrieve_INT32_MIN\r\n| ...\r\n```\r\n\r\nSince the patterns representing the types of `foo` and `bar` are the same it's possible in principle for the compiler to merge the branches.  At present it doesn't do this, however, and so the destructuring code is repeated in each branch.\r\n\r\n```\r\n(stringswitch name/1121\r\n case \"retrieve_INT32_MAX\":\r\n  (switch* t/1122\r\n   case tag 0: (exit 1)\r\n   case tag 1:\r\n    (if (isint (field 0 t/1122))\r\n      (let (match/1321 =a (field 1 t/1122))\r\n        (switch* match/1321\r\n         case tag 0:\r\n          (let (match/1322 =a (field 0 match/1321))\r\n            (switch match/1322\r\n             case tag 0:\r\n              (if (!= (field 0 match/1322) 15) (exit 1)\r\n                (function prim/1251\r\n                  (cstubs_tests_23_retrieve_INT32_MAX\r\n                    prim/1251)))\r\n             default: (exit 1)))\r\n         case tag 1: (exit 1)))\r\n      (exit 1)))\r\n case \"retrieve_INT32_MIN\":\r\n  (switch* t/1122\r\n   case tag 0: (exit 1)\r\n   case tag 1:\r\n    (if (isint (field 0 t/1122))\r\n      (let (match/1325 =a (field 1 t/1122))\r\n        (switch* match/1325\r\n         case tag 0:\r\n          (let (match/1326 =a (field 0 match/1325))\r\n            (switch match/1326\r\n             case tag 0:\r\n              (if (!= (field 0 match/1326) 15) (exit 1)\r\n                (function prim/1252\r\n                  (cstubs_tests_22_retrieve_INT32_MIN\r\n                    prim/1252)))\r\n             default: (exit 1)))\r\n         case tag 1: (exit 1)))\r\n      (exit 1)))\r\n```\r\n\r\nWe can help the compiler out by changing the generated patterns so that the string appears rightmost :\r\n\r\n```ocaml\r\nlet foreign : type a b. string -> (a -> b) Ctypes.fn -> (a -> b) =\r\n  fun name t -> match t, name with\r\n| CI.Function (CI.Void, CI.Returns (CI.Primitive CI.Int32_t)),\r\n  \"retrieve_INT32_MAX\" ->\r\n  cstubs_tests_23_retrieve_INT32_MAX\r\n| CI.Function (CI.Void, CI.Returns (CI.Primitive CI.Int32_t)),\r\n  \"retrieve_INT32_MIN\" ->\r\n  cstubs_tests_22_retrieve_INT32_MIN,\r\n| ...\r\n```\r\n\r\nNow the branches that destructure the type representation are merged in the generated code:\r\n\r\n```\r\n(switch t/1122 \r\ncase tag 0: (exit 1)\r\ncase tag 1:\r\n  (if (isint (field 0 t/1122))\r\n    (let (match/1277 (field 1 t/1122))\r\n      (switch match/1277 \r\n      case tag 0:\r\n        (let (match/1278 (field 0 match/1277))\r\n          (switch match/1278 \r\n          ...\r\n          case tag 0:\r\n            (switch (field 0 match/1278) \r\n            ...\r\n            case int 15:\r\n              (switch name/1121\r\n               case \"retrieve_INT32_MAX\":\r\n                (closure \r\n                  (fun camlGenerated_bindings__fun_1328\r\n                    1  prim/1251\r\n                    (cstubs_tests_23_retrieve_INT32_MAX\r\n                      prim/1251)) )\r\n               case \"retrieve_INT32_MIN\":\r\n                (closure \r\n                  (fun camlGenerated_bindings__fun_1330\r\n                    1  prim/1252\r\n                    (cstubs_tests_22_retrieve_INT32_MIN\r\n                      prim/1252)) )\r\n               default: (exit 1))\r\n```\r\n\r\nThis appears to lead to a significant reduction in code size.  For example, the stripped object file for the `test-value_printing` bindings shrinks from 12K to 8.5K with this change.\r\n\r\nWe should apply this change to the code generated for function, constant and inverted bindings.\r\n"}}