Hacker Newsnew | past | comments | ask | show | jobs | submit | zyedidia's commentslogin

I think the only "C replacement" that is comparable in complexity to C is [Hare](https://harelang.org/), but several shortcomings make it unsuitable as an actual C replacement in many cases (little/no multithreading, no support for macOS/Windows, no LLVM or GCC support, etc.).


And why do you think Zig (and Odin, but I'm not really familiar with that one) is not comparable in complexity to C? If you start with C, replace the preprocessor language with the host language, replace undefined behavior with illegal behavior (panics in debug builds), add different pointer types for different types of pointers (single object pointers, many object pointers, fat many object pointers (slices), nullable pointers), and make a few syntactic changes (types go after the names of values in declarations, pointer dereference is a postfix operator, add defer to move expressions like deallocation to the end of the scope) and write a new standard library, you pretty much have Zig.


I don't think there are any plans for Ladybird to have a JIT compiler (they used to have one but decided to remove it) [1, 2], so it's not clear to me that this performance gap will be improved anytime soon (if ever).

[1]: https://youtu.be/dKHopzDtElY?si=jc3ho2NT4vPTbXBz&t=14

[2]: See FAQ here https://awesomekling.github.io/Ladybird-a-new-cross-platform...


> I don't think there are any plans for Ladybird to have a JIT compiler (they used to have one but decided to remove it) [1, 2], so it's not clear to me that this performance gap will be improved anytime soon (if ever).

How does that make this comparison make any sense?

Point is, this isn't a Servo vs Ladybird comparison. It's a Mozilla vs Ladybird comparison.


I read here recently that Ladybird is prepared to switch to Swift rather than C++. I can't seem to find the relevant sources; can anyone confirm?


Yes, here[0]. Although, it's not anywhere close to being used for everyday things. There are blockers listed in their GitHub issues and various issues posted to the Swift forums.

[0]: https://xcancel.com/awesomekling/status/1822236888188498031


Thank you! I'd imagine performance-sensitive components in the engine need to remain in C++ (or a similar systems language) right? However, I'm not privy to Swift's runtime benchmarks.


Swift has been adding support for things like non-copyable types that should help with writing performance sensitive parts.


> imagine performance-sensitive components in the engine need to remain in C++

I'd imagine so, yes. I think the vision is to use Swift in "risky" areas like parsing data for example. Probably much more too, but the big hitters would be safety critical areas I think.


What is the recommended way to use defer to free values only on an error path (rather than all paths)? Currently I use goto for this:

    void* p1 = malloc(...);
    if (!p1) goto err1;
    void* p2 = malloc(...);
    if (!p2) goto err2;
    void* p3 = malloc(...);
    if (!p3) goto err3;

    return {p1, p2, p3};

    err3: free(p2);
    err2: free(p1);
    err1: return NULL;
With defer I think I would have to use a "success" boolean like this:

    bool success = false;

    void* p1 = malloc(...);
    if (!p1) return NULL;
    defer { if (!success) free(p1) }

    void* p2 = malloc(...);
    if (!p2) return NULL;
    defer { if (!success) free(p2) }

    void* p3 = malloc(...);
    if (!p3) return NULL;
    defer { if (!success) free(p3) }

    success = true;
    return {p1, p2, p3};
I'm not sure if this has really improved things. I do see the use-case for locks and functions that allocate/free together though.


Can also use a different variable name for the success case and null out any successfully consumed temporaries.

    void* p1 = malloc();
    if (!p1) return failure;
    defer { free(p1); }
    ...
    someOther->pointer = p1;
    p1 = NULL;
    return success;


I don't even bother with `error1`, `error2`, ... `errorN`.

I initialise all pointers to NULL at the top of the function and use `goto cleanup`, which cleans up everything that is not being returned ... because `free(some_ptr)` where `some_ptr` is NULL is perfectly legal.


I'm not sure I'd do either for this trivial case, but it might make sense where the cleanup logic is more complex?

    void* p1 = malloc(...);
    void* p2 = malloc(...);
    void* p3 = malloc(...);

    if(p1 && p2 && p3)
      return {p1, p2, p3};

    free(p3);
    free(p2);
    free(p1);
    return NULL;


That is a well structure system, yes Both cleanup for error and allocation happens in the same place

That means you won't forget to call it, and the success flag is an obvious way to ha dle it


I think data races can cause undefined behavior in Go, which can cause memory safety to break down. See https://research.swtch.com/gorace for details.


It sounds like what you're describing is one-time allocation, and I think it's a good idea. There is some work on making practical allocators that work this way [1]. For long-running programs, the allocator will run out of virtual address space and then you need something to resolve that -- either you do some form of garbage collection or you compromise on safety and just start reusing memory. This also doesn't address spatial safety.

[1]: https://www.usenix.org/system/files/sec21summer_wickman.pdf


> For long-running programs, the allocator will run out of virtual address space and then you need something to resolve that -- either you do some form of garbage collection or you compromise on safety and just start reusing memory

Or you destroy the current process after you marshall the data that should survive into a newly forked process. Side benefit: this means you get live upgrade support for free, because what is a live upgrade but migrating state to a new process with updated code?


Oh, nifty! I guarantee you anyone else discussing this has put more than my 5 minutes' worth of thought into it.

Yeah, if you allow reuse then it wouldn't be a guarantee. I think it'd be closer to the effects of ASLR, where it's still possible to accidentally still break things, just vastly less likely.


The bug used by that repository [1] isn't the only one that can be used to escape the Safe Rust type system. There are a couple others I've tried [2] [3], and the Rust issue tracker currently lists 92 unsoundness bugs (though only some of them are general-purpose escapes), and that's only the ones we know about.

These bugs are not really a problem in practice though as long as the developer is not malicious. However, they are a problem for supply chain security or any case where the Rust source is fully untrusted.

[1]: https://github.com/rust-lang/rust/issues/25860

[2]: https://github.com/rust-lang/rust/issues/57893

[3]: https://github.com/rust-lang/rust/issues/133361


This is very cool! I'm always on the lookout for extensible assemblers. I especially want one that can handle a normalized subset of GNU assembly so that it can be used on the output of LLVM or GCC (using existing assembly languages, but assembling them in non-standard ways or with extensions).


Is there any AOT WebAssembly compiler that can compile Wasm used by websites? I tried locally compiling the Photoshop Wasm module mentioned in the article but the compilers I tried (Wasmtime, wasm2c, WAMR) all complained about some unsupported Wasm extension/proposal being required (exceptions seems like the blocker on wasmtime, and the others gave cryptic error messages).

Is it really the case that browsers have default-enabled all sorts of extensions that are not yet widely supported by the rest of the ecosystem?


> Is there any AOT WebAssembly compiler that can compile Wasm used by websites?

This doesn't really make sense, given that the wasm used by websites is going to import a bunch of JS functions as dependencies. You're not going to have those available in any native environment.

> Is it really the case that browsers have default-enabled all sorts of extensions that are not yet widely supported by the rest of the ecosystem?

Yes

Photoshop in particular is a good example of a bleeding edge wasm app - browsers had to relax restrictions on things like function pointer table size in order for it to work. So I wouldn't expect it to build and run anywhere outside of v8 or spidermonkey.


I think one of the interesting aspects of WebAssembly compared to JavaScript is that it can be efficiently AOT-compiled. I've been interested in investigating AOT compilation for a browser (perhaps there is a distant/alternative future where web browsing does not require a JIT), but maybe Wasm AOT compilers aren't really there yet.


Functionally what browsers do under the hood is AOT compilation but not in the way that i.e. Wasmtime is. The following is my knowledge that may no longer be accurate, but this is the sort of model we planned for when designing WASM to begin with:

Browsers do a on-demand 'first tier' compilation for fast startup, and in the background using threads they do a high quality AOT compilation of the whole module. That high quality AOT compilation is cached and used for future page loads.

It is possible to use a JIT model for this but AFAIK it is typically not done. In some configurations (i.e. lockdown mode) WASM is interpreted instead of AOT compiled.


> It is possible to use a JIT model for this but AFAIK it is typically not done.

Isn't this what the last line of the article is hinting at? > our WebAssembly team is making great progress rearchitecting the Wasm compiler pipeline. This work will make it possible to Ion-compile individual Wasm functions as they warm up instead of compiling everything immediately.


I believe this is still true. Originally you could store a compiled wasm module in IndexedDB and cache it manually, but that was removed in favor of {instantiate,compile}Streaming, which take a HTTP response and hook into the existing HTTP caching layer, which was already storing the compliation output for JS.


No, I think most of the AOT compilers in practice are a bit behind V8 and/or Spidermonkey for newer features. Realistically, most development driving new WASM features is motivated by website-ish use cases. Exception handling in particular is still not standardized so I guess it's expected that the browser engines would be the one to have the most evolving support (and the ability to test it thoroughly) because people want that platform as their inevitable target anyway.


> Is it really the case that browsers have default-enabled all sorts of extensions that are not yet widely supported by the rest of the ecosystem?

I don't know the answer, but it would be hard to blame them for following normal browser development practices on the standard they created for the purpose of being in browsers.


Fair enough. I think it would be unfortunate if the WebAssembly language in browsers were a significantly different language than WebAssembly outside of browsers (just referring to language itself, not the overall runtime system). I don't think that has quite happened, and the outer ecosystem can probably catch up, but it worries me.


Non-browser environments are a little behind on the Wasm standard, but not by much. E.g. wasmtime has now landed support for Wasm GC. AFAIK they implement all phase 4 proposals. Wizard implements all the Phase 4 proposals as well. The Wasm 3.0 spec will be out soon, which will be a big milestone to motivate Wasm engines outside the Web to catch up.


We already had plenty of bytecode formats outside the browser since UNCOL was an idea in 1958, including as replacement for Assembly, with microcoded CPUs.

Now we get a couple of startups trying to make WebAssembly outside of the browser as if was a novel idea, never done before.


I am excitedly awaiting the full release of ASL1 from Arm. I wonder if anyone with more knowledge might be able to comment on how it compares with Sail and/or when we might expect to see a full Arm specification in ASL1 (as opposed to the current spec which is normal ASL and appears to be incompatible with the upcoming version). Perhaps in the future there might also be a RISC-V specification written in ASL1.


Sail is pretty similar to ASL (both current ASL and ASL 1.0) except that (1) it has a more expressive type system, so that bitvector lengths can all be statically checked, (2) it has proper tagged unions and pattern matching, and (3) there's a wide range of open-source tooling available, for execution, specification coverage, generating emulators, integrating with relaxed concurrency models, generating theorem-prover definitions, etc. We've recently updated the Sail README, which spells some of this out: https://github.com/rems-project/sail .

As Alastair Reid says, one of the main things missing in the current RISC-V specification documents is simply that the associated Sail definitions are not yet interspersed with the prose instruction descriptions. The infrastructure to do that has been available for some time, in the Sail AsciiDoc support by Alasdair Armstrong (https://github.com/Alasdair/asciidoctor-sail/blob/master/doc...) and older LaTeX versions by Prashanth Mundkur and Alasdair (https://github.com/rems-project/riscv-isa-manual/blob/sail/r...).


Note that herd contains an ASL1 implementation. https://github.com/herd/herdtools7/tree/master/asllib


The reason is because the ARM `brk #0x1` instruction doesn't set the exception return address to the next instruction, so the debugger will just return to the breakpoint when it tries to resume, instead of running the next instruction. Recent versions of LLDB will recognize `brk #0xf000` and automatically step over it when returning, but I don't think GDB does this. With GDB you would have to run something manually like `jump *($pc + 4)` to resume at the next instruction. Clang's `__builtin_debugtrap()` will generate `brk #0xf000` automatically.


I've been using this macro with GCC / GDB for years without running into the issue you're describing:

#define DEBUG_BREAK() do{__asm__("BKPT");} while(0)

I can continue just fine with it. Granted, this is on the various Cortex M0/M3/M4 chips, so I can't say for sure if it works on any of the bigger, fancier ARMs.


I think it's a difference between ARMv8 and ARMv6/7 (I believe BKPT on ARMv6/7 sets the exception return address to `addr(instruction)+4`).


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: