March 2023
Week of 2023-03-27β
-
π’ Parameter names are no longer load-bearing in function signatures. This gives more flexibility in defining higher-order functions, because the functions passed as parameters do not need their parameter names to match.
# Define a higher-order function... fn generator[ func: __mlir_type[`!kgen.signature<`, Int, `>() -> !kgen.none`] ](): pass # Int parameter is named "foo". fn f0[foo: Int](): pass # Int parameter is named "bar". fn f1[bar: Int](): pass fn main(): # Both can be used as `func`! generator[f0]() generator[f1]()Stay tuned for improved function type syntax...
-
π’ Two magic operators, named
__get_lvalue_as_address(x)and__get_address_as_lvalueconvert stored LValues to and from!kgen.pointertypes (respectively). This is most useful when using thePointer[T]library type. ThePointer(to=lvalue)method uses the first one internally. The second one must currently be used explicitly, and can be used to project a pointer to a reference that you can pass around and use as a self value, for example:# "Replacement new" SomeHeavy value into the memory pointed to by a # Pointer[SomeHeavy]. __get_address_as_lvalue(somePointer.value) = SomeHeavy(4, 5)Note that
__get_address_as_lvalueassumes that there is already a value at the specified address, so the assignment above will run theSomeHeavydestructor (if any) before reassigning over the value. -
The
(((x)))syntax is __mlir_op has been removed in favor of__get_lvalue_as_addresswhich solves the same problem and is more general. -
π’ When using a mutable
selfargument to a struct__init__method, it now must be declared with&, like any other mutable method. This clarifies the mutation model by making__init__consistent with other mutating methods. -
π Add variadic string join function.
-
π Default initialize values with 0 or null if possible.
-
π Add compressed, aligned, and mask store intrinsics.
Week of 2023-03-20β
-
Initial
Stringtype is added to the standard library with some very basic methods. -
Add
DimListto remove the need to use an MLIR list type throughout the standard library. -
π’ The
__clone__method for copying a value is now named__copy__to better follow Python term of art. -
π’ The
__copy__method now takes its self argument as a "read" value, instead of taking it by reference. This makes it easier to write, works for@register_passabletypes, and exposes more optimization opportunities to the early optimizer and dataflow analysis passes.# Before: fn __clone__(inout self) -> Self: ... # After: fn __copy__(self) -> Self: ... -
π’ A new
@register_passable("trivial")may be applied to structs that have no need for a custom__copy__or__del__method, and whose state is only made up of@register_passable("trivial")types. This eliminates the need to define__copy__boilerplate and reduces the amount of IR generated by the compiler for trivial types likeInt. -
You can now write back to attributes of structs that are produced by a computed lvalue expression. For example
a[i].x = ..works whena[i]is produced with a__getitem__/__setitem__call. This is implemented by performing a read ofa[i], updating the temporary, then doing a writeback. -
The remaining hurdles to using non-parametric,
@register_passabletypes as parameter values have been cleared. Types likeIntshould enjoy full use as parameter values. -
Parameter pack inference has been added to function calls. Calls to functions with parameter packs can now elide the pack types:
fn foo[*Ts: AnyType](*args: *Ts): pass foo(1, 1.2, True, "hello")Note that the syntax for parameter packs has been changed as well.
-
π Add the runtime string type.
-
π Introduce the DimList struct to remove the need to use low-level MLIR operations.
Week of 2023-03-13β
-
π’ Initializers for structs now use
__init__instead of__new__, following standard practice in Python. You can write them in one of two styles, either traditional where you mutate self:fn __init__(self, x: Int): self.x = xor as a function that returns an instance:
fn __init__(x: Int) -> Self: return Self {x: x}Note that
@register_passabletypes must use the later style. -
π’ The default argument convention is now the
borrowedconvention. A "read" argument is passed like a C++const&so it doesn't need to invoke the copy constructor (aka the__clone__method) when passing a value to the function. There are two differences from C++const&:- A future borrow checker will make sure there are no mutable aliases with an immutable borrow.
@register_passablevalues are passed directly in an SSA register (and thus, usually in a machine register) instead of using an extra reference wrapper. This is more efficient and is the 'right default' for@register_passablevalues like integers and pointers.
This also paves the way to remove the reference requirement from
__clone__method arguments, which will allow us to fill in more support for them. -
Support for variadic pack arguments has been added to Mojo. You can now write heterogeneous variadic packs like:
fn foo[*Ts: AnyType](args*: Ts): pass foo[Int, F32, String, Bool](1, 1.5, "hello", True) -
The
ownedargument convention has been added. This argument convention indicates that the function takes ownership of the argument and is responsible for managing its lifetime. -
The
borrowedargument convention has been added. This convention signifies the callee gets an immutable shared reference to a value in the caller's context. -
π Add the
getenvfunction to theOSmodule to enable getting environment variables. -
π Enable the use of dynamic strides in
NDBuffer.
Week of 2023-03-06β
-
π’ Support added for using capturing async functions as parameters.
-
π’ Returning result parameters has been moved from
returnstatements to a newparam_returnstatement. This allows returning result parameters from throwing functions:@raises fn foo[() -> out: Int](): param_return[42] raise Error()And returning different parameters along
@parameter ifbranches:fn bar[in: Bool -> out: Int](): @parameter if in: param_return[1] else: param_return[2] -
π’ Mojo now supports omitting returns at the end of functions when they would not reachable. For instance,
fn foo(cond: Bool) -> Int: if cond: return 0 else: return 1 fn bar() -> Int: while True: pass -
String literals now support concatenation, so
"hello " "world"is treated the same as"hello world". -
Empty bodies on functions, structs, and control flow statements are no longer allowed. Please use
passin them to explicitly mark that they are empty, just like in Python. -
π’ Structs in Mojo now default to living in memory instead of being passed around in registers. This is the right default for generality (large structures, structures whose pointer identity matters, etc) and is a key technology that enables the borrow model. For simple types like
IntandSIMD, they can be marked as@register_passable.Note that memory-only types currently have some limitations: they cannot be used in generic algorithms that take and return a
!mlirtypeargument, and they cannot be used in parameter expressions. Because of this, a lot of types have to be marked@register_passablejust to work around the limitations. We expect to enable these use-cases over time. -
π’ Mojo now supports computed lvalues, which means you can finally assign to subscript expressions instead of having to call
__setitem__explicitly.Some details on this: Mojo allows you to define multiple
__setitem__overloads, but will pick the one that matches your__getitem__type if present. It allows you to pass computed lvalues into inout arguments by introducing a temporary copy of the value in question. -
Mojo now has much better support for using register-primary struct types in parameter expressions and as the types of parameter values. This will allow migration of many standard library types away from using bare MLIR types like
__mlir_type.indexand towards usingInt. This moves us towards getting rid of MLIR types everywhere and makes struct types first-class citizens in the parameter system. -
π Add a
sortfunction. -
π Add non-temporal store to enable cache bypass.
Was this page helpful?
Thank you! We'll create more content like this.
Thank you for helping us improve!