{"action":"created","issue":{"url":"https://api.github.com/repos/ocaml/ocaml/issues/199","labels_url":"https://api.github.com/repos/ocaml/ocaml/issues/199/labels{/name}","comments_url":"https://api.github.com/repos/ocaml/ocaml/issues/199/comments","events_url":"https://api.github.com/repos/ocaml/ocaml/issues/199/events","html_url":"https://github.com/ocaml/ocaml/pull/199","id":86675909,"number":199,"title":"Dynamically extract return location","user":{"login":"def-lkb","id":1048096,"avatar_url":"https://avatars.githubusercontent.com/u/1048096?v=3","gravatar_id":"","url":"https://api.github.com/users/def-lkb","html_url":"https://github.com/def-lkb","followers_url":"https://api.github.com/users/def-lkb/followers","following_url":"https://api.github.com/users/def-lkb/following{/other_user}","gists_url":"https://api.github.com/users/def-lkb/gists{/gist_id}","starred_url":"https://api.github.com/users/def-lkb/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/def-lkb/subscriptions","organizations_url":"https://api.github.com/users/def-lkb/orgs","repos_url":"https://api.github.com/users/def-lkb/repos","events_url":"https://api.github.com/users/def-lkb/events{/privacy}","received_events_url":"https://api.github.com/users/def-lkb/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":null,"comments":6,"created_at":"2015-06-09T17:38:16Z","updated_at":"2015-06-15T15:37:52Z","closed_at":null,"pull_request":{"url":"https://api.github.com/repos/ocaml/ocaml/pulls/199","html_url":"https://github.com/ocaml/ocaml/pull/199","diff_url":"https://github.com/ocaml/ocaml/pull/199.diff","patch_url":"https://github.com/ocaml/ocaml/pull/199.patch"},"body":"The following proposal adds a \"%retloc\" primitive, returning an abstract location of the caller of the current function.\r\n\r\n```ocaml\r\ntype retloc\r\nexternal return_location : unit -> retloc = \"%retloc\"\r\n```\r\n\r\nThe location can eventually be turned into a transparent representation later:\r\n```ocaml\r\nexternal decode_return_location : retloc -> (string * int * int) option\r\n```\r\n\r\nWhich will succeed if debug information are available. The first primitive is designed to be cheap and operate without allocation, and is appropriate to use on a fast path.\r\n\r\n## Intended use\r\n\r\nAnother approach to get location from user source files.\r\nWhen implementing a library, it is convenient to report error messages referring to locations from the user source files. It is not possible to offer such guarantees with backtraces.\r\n\r\nLwt used a syntactic preprocessor to track locations, my proposal https://github.com/ocaml/ocaml/pull/126 used a type directed approach, this proposal uses a runtime approach.\r\n\r\nTracking and threading locations can be done in a natural ocaml-style:\r\n```ocaml\r\nlet bind ?(location=return_location ()) m f = …\r\n\r\nlet map ?(location=return_location ()) f m =\r\n  bind ~location m (fun x -> return (f x ))\r\n```\r\n\r\nHowever be aware that this trick breaks abstraction:\r\n```ocaml\r\nlet here () = return_location ()\r\n```\r\n\r\nCalling `here` (returning the location where here is called) doesn't have the same effect as calling `return_location` (returning the location of the caller of the current body).\r\nInstead, the `here` defined above behaves similarly to `__POS__` primitive.\r\n\r\n## Implementation\r\n\r\n```ocaml\r\ntype retloc = int\r\nexternal return_location : unit -> retloc = \"%retloc\"\r\nexternal decode_return_location : retloc -> (string * int * int) option\r\n```\r\n\r\nAt a first approximation, `return_location` moves the return address of the current stack frame to an ocaml value. The offset to this address is known statically, so this issue a single instruction.\r\nThe value is then turned into an ocaml integer by setting the LSB.\r\n\r\nReason: this is done relying on the assumption that there won't be two call sites separated by a single byte, so return addresses are still unique when dropping one bit. Shifting however, could more easily become a problem on 32 bits platform.\r\n\r\n`decode_return_location` then looks up the ocaml frame descriptors table to turn the return address into an explicit location (following the same process as backtraces).\r\n\r\nHowever, the inliner might decide to move the call to the primitive into its parent body, where the return address would not be the expected one.\r\nInlining of retloc primitive is treated specially: a (string * int * int) option constant is statically defined, storing the same value as what `decode_return_location` would have returned.\r\n\r\nAs such, the actual representation is closer to the pseudocaml:\r\n```ocaml\r\ntype retloc = int | (string * int * int) option\r\n```\r\nStill no allocations and very cheap code on hot path!\r\n\r\n## TODOs\r\n\r\n1. Support more platforms\r\nOnly AMD64 native backend is supported, support for bytecode and other architectures should be added.\r\n\r\n2. Representations might need some work.\r\nIs losing one bit an issue?\r\nIf one is trying to keep track of a moving location in a reference, the fact that retloc is not always an immediate integer implies a call to caml_modify that one might prefer to avoid on a fast path. (Values returned by retloc can either be immediates or roots -- until a dynunlink proposal appears -- caml_modify could be skipped)\r\n\r\n"},"comment":{"url":"https://api.github.com/repos/ocaml/ocaml/issues/comments/112113097","html_url":"https://github.com/ocaml/ocaml/pull/199#issuecomment-112113097","issue_url":"https://api.github.com/repos/ocaml/ocaml/issues/199","id":112113097,"user":{"login":"chambart","id":2196625,"avatar_url":"https://avatars.githubusercontent.com/u/2196625?v=3","gravatar_id":"","url":"https://api.github.com/users/chambart","html_url":"https://github.com/chambart","followers_url":"https://api.github.com/users/chambart/followers","following_url":"https://api.github.com/users/chambart/following{/other_user}","gists_url":"https://api.github.com/users/chambart/gists{/gist_id}","starred_url":"https://api.github.com/users/chambart/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/chambart/subscriptions","organizations_url":"https://api.github.com/users/chambart/orgs","repos_url":"https://api.github.com/users/chambart/repos","events_url":"https://api.github.com/users/chambart/events{/privacy}","received_events_url":"https://api.github.com/users/chambart/received_events","type":"User","site_admin":false},"created_at":"2015-06-15T15:37:52Z","updated_at":"2015-06-15T15:37:52Z","body":"I don't see why you consider that this is some syntactic information.\r\n```ocaml\r\nlet f g x = g x\r\nlet h g x = g x\r\nlet stuff x = return_location (),x\r\nlet _ = f stuff 1, g stuff 2\r\n```\r\nthe return location will be different for both calls of `stuff`.\r\n\r\nFor the behavior relative to program transformation (I suppose that you mean those done by the backend, not by the user), I consider that it is a bug if it differ (backtraces shouldn't drop inlined functions). "}}