diff --git a/FIX.org b/FIX.org new file mode 100644 index 0000000..9c083d1 --- /dev/null +++ b/FIX.org @@ -0,0 +1,26 @@ +* Stuff I've found +** Why do I need to call flan-dev to open another window? + let: flan: the program exited; restart flan dev +** I can't eval a toplevel defvar, need to eval-defun (C-c C-c) +For Flan’s intended live-program workflow, C-x C-e on any complete top-level form should do the natural thing: + + - expression → compile/run temporary thunk; print its value + - defn, defvar, defmacro, etc. → compile/install it; report what changed + +The compiler already has both paths. The current split is an editor/UI artifact: C-x C-e is wired directly to eval-expr, while C-c C-c is wired to declaration reload. It is not a fundamental limitation. + +A good fix would make C-x C-e context-aware: if the enclosing form is top-level, send it through the declaration evaluator; otherwise use expression evaluation. Then C-c C-c can remain a convenient explicit “reload this definition” alias, but not the only way defvar works. +** I can't eval a top level Vec +slurp returns (Vec u8), an owning, move-only buffer. Flan currently forbids every move-only global because it has no global ownership/lifetime model: any function could read and free it, while ownership tracking only exists within one function. + +For data that is fixed at build time, use an embedded immutable array instead: + +(defconst the-data (embed "game-data.edn")) + +That produces a fixed [u8], not a heap-owning Vec, so it can live globally. It also resolves relative to sand.flan. + +If game-data.edn genuinely must be loaded at runtime, then today it has to be owned by a local—typically load it in main and pass it through the functions that need it. For a game-wide runtime-owned data asset, that is a missing language/runtime feature, not a bad use case on your part. +** The edn module seems to need a struct declaration, it should do both; go into a struct but also return a Map with Vecs and Sets when we don't provide a type +** defenum needs optional autoincrementing discriminants +** We need a javascript backend so we can reach the world +** We need to have C-style unions, maybe those are called defunion, and then sum types are defdata or deftype diff --git a/MY-NOTES.org b/MY-NOTES.org index dc9dc25..b410d50 100644 --- a/MY-NOTES.org +++ b/MY-NOTES.org @@ -1,4 +1 @@ * Additional things -** Why do we have prints dedicted per type? That's terrible, what do we need to have proper (println) work for everything? -** The current std is suffering from the same problem, functions should be generic? Why is it like this? -** Why do we have =at= and =nth= if they're the same? Pick one, maybe =at=. For all STD functions make sure we don't have redundancy, even if it matches clojure or lisp diff --git a/emacs/flan-dev.el b/emacs/flan-dev.el index 6f0b0ce..da3335c 100644 --- a/emacs/flan-dev.el +++ b/emacs/flan-dev.el @@ -570,13 +570,19 @@ When called interactively while a daemon this Emacs started is alive, asks before stopping it and switching programs. A noninteractive call still refuses: callers cannot silently discard a running program's state." (interactive - ;; The program last started, where there was one: a restart after a quit is - ;; the common case, and it is rarely the buffer point happens to be in — - ;; you quit from wherever you were reading when you decided to. - (list (read-file-name "flan dev: " nil flan-dev--file t - (and buffer-file-name - (string-suffix-p ".flan" buffer-file-name) - (file-name-nondirectory buffer-file-name))))) + ;; Starting a program is about the buffer the command was called from. + ;; Keeping [flan-dev--file] as DEFAULT here made a previous project win + ;; over the current buffer: after working on sand.flan, invoking this from + ;; ~/Development/gameboy/gameboy.flan still proposed (and could start) + ;; sand.flan. [flan-dev-restart-program] is the deliberate way to restart + ;; the previous program; an ordinary M-x command must not do that. + (let ((file (and buffer-file-name + (string-suffix-p ".flan" buffer-file-name) + (expand-file-name buffer-file-name)))) + (list (read-file-name "flan dev: " + (and file (file-name-directory file)) + file t + (and file (file-name-nondirectory file)))))) (when (process-live-p flan-dev--daemon) ;; `interactive' has already read FILE. Refusing only here used to make ;; that selection look as though it had been ignored: the old daemon kept diff --git a/emacs/flan-lower.el b/emacs/flan-lower.el index 57efde4..cd20c29 100644 --- a/emacs/flan-lower.el +++ b/emacs/flan-lower.el @@ -562,6 +562,16 @@ that is felt." (setq-local outline-regexp "[▸▾] ") (setq-local outline-level (lambda () 1)) (outline-minor-mode 1) + ;; Minor-mode bindings take precedence over this mode's map. Outline owns + ;; TAB by default, so give this buffer its intended folding keys back + ;; without changing Outline's bindings anywhere else. + (let ((map (copy-keymap outline-minor-mode-map))) + (define-key map (kbd "TAB") #'flan-lower-toggle) + (define-key map [backtab] #'flan-lower-cycle-all) + (setq-local minor-mode-overriding-map-alist + (cons (cons 'outline-minor-mode map) + (assq-delete-all 'outline-minor-mode + minor-mode-overriding-map-alist)))) (add-hook 'kill-buffer-hook #'flan-lower--clean nil t)) ;;;###autoload diff --git a/emacs/test-flan-dev.el b/emacs/test-flan-dev.el index 8f7dd7d..b59b658 100644 --- a/emacs/test-flan-dev.el +++ b/emacs/test-flan-dev.el @@ -853,6 +853,8 @@ is written instead — the real `message' call the real command makes." (with-current-buffer flan-lower-buffer (test-flan--check "C-c C-l opens a lowering buffer in its own mode" (derived-mode-p 'flan-lower-mode)) + (test-flan--check "TAB is the lowering buffer's toggle, not Outline's" + (eq (key-binding (kbd "TAB")) #'flan-lower-toggle)) ;; The claim the header makes, which is the opposite of the one ;; `flan-disassemble' makes, and the reason both commands exist. (test-flan--check "whose header says it is the file and not the program" diff --git a/lib/emit.ml b/lib/emit.ml index cb4a8a2..337ea9e 100644 --- a/lib/emit.ml +++ b/lib/emit.ml @@ -577,6 +577,25 @@ let ins f fmt = Printf.ksprintf (fun s -> if f.live then Buffer.add_string f.b (" " ^ s ^ f.dloc ^ "\n")) fmt +(* A fixed array has no padding between elements, so its zero value is exactly + a run of zero bytes. Naming that operation lets LLVM choose its bulk-clear + implementation instead of expanding a large aggregate store into one store + per element. Keep small arrays as typed stores: their inline code is + cheaper than a call on targets which do not inline the intrinsic. *) +let bulk_zero_min_bytes = 64 + +let emit_bulk_zero f ptr ty = + match ty with + | Types.Array _ -> + let size, align = lay f.md ty in + if size >= bulk_zero_min_bytes then begin + ins f + "call void @llvm.memset.p0.i64(ptr align %d %s, i8 0, i64 %d, i1 false)" + align ptr size; + true + end else false + | _ -> false + let term f fmt = Printf.ksprintf (fun s -> @@ -1084,8 +1103,11 @@ and value_at f (e : Tast.expr) : string = "zeroinitializer" | Tast.Set (p, v) -> let ptr, ty = place f p in - let v' = value f v in - ins f "store %s %s, ptr %s" (ll ty) v' ptr; + (match v.Tast.e with + | Tast.Zero _ when emit_bulk_zero f ptr ty -> () + | _ -> + let v' = value f v in + ins f "store %s %s, ptr %s" (ll ty) v' ptr); "zeroinitializer" | Tast.Make (_, fields) -> aggregate f e.Tast.ty fields | Tast.MakeCase (uname, case, fields) -> @@ -2602,6 +2624,7 @@ let header = {|; Generated by flan. The layout is C's: no object headers anywher %flanframe = type { ptr, ptr, ptr } @flan_frame_head = external global ptr +declare void @llvm.memset.p0.i64(ptr nocapture writeonly, i8, i64, i1 immarg) declare void @flan_rt_init(i32, ptr) declare void @flan_argv(ptr) declare void @flan_write_stdout(ptr, i64)