Skip to content

Commit

Permalink
Even More Opcodes (#103)
Browse files Browse the repository at this point in the history
Closes #62, adding `floor`, `ceil`, `round`, `atan2`

(To unblock this implementation, I'm using function calls instead of
writing assembly)
  • Loading branch information
mkeeter authored May 12, 2024
1 parent f15e65d commit 4cc5f6a
Show file tree
Hide file tree
Showing 22 changed files with 934 additions and 4 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
- Make `RenderMode` trait methods static, because they weren't using `&self`
- Change signature of `fidget::render::render2d` to pass the mode only as a
generic parameter, instead of an argument
- Add new operations: `floor`, `ceil`, `round`, `atan2`

# 0.2.6
This is a relatively small release; there are a few features to improve the
Expand Down
18 changes: 18 additions & 0 deletions fidget/src/core/compiler/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ impl<const N: usize> RegisterAllocator<N> {
SsaOp::RecipReg(out, arg) => (out, arg, RegOp::RecipReg),
SsaOp::SqrtReg(out, arg) => (out, arg, RegOp::SqrtReg),
SsaOp::SquareReg(out, arg) => (out, arg, RegOp::SquareReg),
SsaOp::FloorReg(out, arg) => (out, arg, RegOp::FloorReg),
SsaOp::CeilReg(out, arg) => (out, arg, RegOp::CeilReg),
SsaOp::RoundReg(out, arg) => (out, arg, RegOp::RoundReg),
SsaOp::SinReg(out, arg) => (out, arg, RegOp::SinReg),
SsaOp::CosReg(out, arg) => (out, arg, RegOp::CosReg),
SsaOp::TanReg(out, arg) => (out, arg, RegOp::TanReg),
Expand Down Expand Up @@ -278,6 +281,9 @@ impl<const N: usize> RegisterAllocator<N> {
| SsaOp::RecipReg(..)
| SsaOp::SqrtReg(..)
| SsaOp::SquareReg(..)
| SsaOp::FloorReg(..)
| SsaOp::CeilReg(..)
| SsaOp::RoundReg(..)
| SsaOp::CopyReg(..)
| SsaOp::SinReg(..)
| SsaOp::CosReg(..)
Expand All @@ -295,6 +301,8 @@ impl<const N: usize> RegisterAllocator<N> {
| SsaOp::MulRegImm(..)
| SsaOp::DivRegImm(..)
| SsaOp::DivImmReg(..)
| SsaOp::AtanImmReg(..)
| SsaOp::AtanRegImm(..)
| SsaOp::MinRegImm(..)
| SsaOp::MaxRegImm(..)
| SsaOp::CompareRegImm(..)
Expand All @@ -308,6 +316,7 @@ impl<const N: usize> RegisterAllocator<N> {
| SsaOp::SubRegReg(..)
| SsaOp::MulRegReg(..)
| SsaOp::DivRegReg(..)
| SsaOp::AtanRegReg(..)
| SsaOp::MinRegReg(..)
| SsaOp::MaxRegReg(..)
| SsaOp::CompareRegReg(..)
Expand Down Expand Up @@ -490,6 +499,9 @@ impl<const N: usize> RegisterAllocator<N> {
SsaOp::DivRegReg(out, lhs, rhs) => {
(out, lhs, rhs, RegOp::DivRegReg)
}
SsaOp::AtanRegReg(out, lhs, rhs) => {
(out, lhs, rhs, RegOp::AtanRegReg)
}
SsaOp::MinRegReg(out, lhs, rhs) => {
(out, lhs, rhs, RegOp::MinRegReg)
}
Expand Down Expand Up @@ -612,6 +624,12 @@ impl<const N: usize> RegisterAllocator<N> {
SsaOp::DivImmReg(out, arg, imm) => {
(out, arg, imm, RegOp::DivImmReg)
}
SsaOp::AtanRegImm(out, arg, imm) => {
(out, arg, imm, RegOp::AtanRegImm)
}
SsaOp::AtanImmReg(out, arg, imm) => {
(out, arg, imm, RegOp::AtanImmReg)
}
SsaOp::MinRegImm(out, arg, imm) => {
(out, arg, imm, RegOp::MinRegImm)
}
Expand Down
27 changes: 27 additions & 0 deletions fidget/src/core/compiler/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,15 @@ macro_rules! opcodes {
#[doc = "Square the given register"]
SquareReg($t, $t),

#[doc = "Returns the largest integer less than or equal to `self`"]
FloorReg($t, $t),

#[doc = "Returns the smallest integer greater than or equal to `self`"]
CeilReg($t, $t),

#[doc = "Returns the nearest integer to `self`. If a value is half-way between two integers, round away from `0.0`."]
RoundReg($t, $t),

#[doc = "Computes the sine of the given register (in radians)"]
SinReg($t, $t),

Expand Down Expand Up @@ -89,6 +98,12 @@ macro_rules! opcodes {
ModRegImm($t, $t, f32),
#[doc = "Take the module (least nonnegative remainder) of an immediate and a register"]
ModImmReg($t, $t, f32),
#[doc = "atan2 of a position `(y, x)` specified as register, register"]
AtanRegReg($t, $t, $t),
#[doc = "atan2 of a position `(y, x)` specified as register, immediate"]
AtanRegImm($t, $t, f32),
#[doc = "atan2 of a position `(y, x)` specified as immediate, register"]
AtanImmReg($t, $t, f32),
#[doc = "Compute the minimum of a register and an immediate"]
MinRegImm($t, $t, f32),
#[doc = "Compute the maximum of a register and an immediate"]
Expand Down Expand Up @@ -158,6 +173,9 @@ impl SsaOp {
| SsaOp::RecipReg(out, ..)
| SsaOp::SqrtReg(out, ..)
| SsaOp::SquareReg(out, ..)
| SsaOp::FloorReg(out, ..)
| SsaOp::CeilReg(out, ..)
| SsaOp::RoundReg(out, ..)
| SsaOp::CopyReg(out, ..)
| SsaOp::SinReg(out, ..)
| SsaOp::CosReg(out, ..)
Expand All @@ -178,6 +196,9 @@ impl SsaOp {
| SsaOp::MulRegReg(out, ..)
| SsaOp::DivRegReg(out, ..)
| SsaOp::SubRegReg(out, ..)
| SsaOp::AtanRegReg(out, ..)
| SsaOp::AtanRegImm(out, ..)
| SsaOp::AtanImmReg(out, ..)
| SsaOp::MinRegImm(out, ..)
| SsaOp::MaxRegImm(out, ..)
| SsaOp::MinRegReg(out, ..)
Expand All @@ -204,6 +225,9 @@ impl SsaOp {
| SsaOp::RecipReg(..)
| SsaOp::SqrtReg(..)
| SsaOp::SquareReg(..)
| SsaOp::FloorReg(..)
| SsaOp::CeilReg(..)
| SsaOp::RoundReg(..)
| SsaOp::CopyReg(..)
| SsaOp::SinReg(..)
| SsaOp::CosReg(..)
Expand All @@ -224,6 +248,9 @@ impl SsaOp {
| SsaOp::DivRegReg(..)
| SsaOp::DivRegImm(..)
| SsaOp::DivImmReg(..)
| SsaOp::AtanRegReg(..)
| SsaOp::AtanRegImm(..)
| SsaOp::AtanImmReg(..)
| SsaOp::CompareRegReg(..)
| SsaOp::CompareRegImm(..)
| SsaOp::CompareImmReg(..)
Expand Down
20 changes: 20 additions & 0 deletions fidget/src/core/compiler/ssa_tape.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,11 @@ impl SsaTape {
SsaOp::DivRegImm,
SsaOp::DivImmReg,
),
BinaryOpcode::Atan => (
SsaOp::AtanRegReg,
SsaOp::AtanRegImm,
SsaOp::AtanImmReg,
),
BinaryOpcode::Min => (
SsaOp::MinRegReg,
SsaOp::MinRegImm,
Expand Down Expand Up @@ -201,6 +206,9 @@ impl SsaTape {
UnaryOpcode::Recip => SsaOp::RecipReg,
UnaryOpcode::Sqrt => SsaOp::SqrtReg,
UnaryOpcode::Square => SsaOp::SquareReg,
UnaryOpcode::Floor => SsaOp::FloorReg,
UnaryOpcode::Ceil => SsaOp::CeilReg,
UnaryOpcode::Round => SsaOp::RoundReg,
UnaryOpcode::Sin => SsaOp::SinReg,
UnaryOpcode::Cos => SsaOp::CosReg,
UnaryOpcode::Tan => SsaOp::TanReg,
Expand Down Expand Up @@ -262,6 +270,9 @@ impl SsaTape {
| SsaOp::SqrtReg(out, arg)
| SsaOp::CopyReg(out, arg)
| SsaOp::SquareReg(out, arg)
| SsaOp::FloorReg(out, arg)
| SsaOp::CeilReg(out, arg)
| SsaOp::RoundReg(out, arg)
| SsaOp::SinReg(out, arg)
| SsaOp::CosReg(out, arg)
| SsaOp::TanReg(out, arg)
Expand All @@ -277,6 +288,9 @@ impl SsaTape {
SsaOp::RecipReg(..) => "RECIP",
SsaOp::SqrtReg(..) => "SQRT",
SsaOp::SquareReg(..) => "SQUARE",
SsaOp::FloorReg(..) => "FLOOR",
SsaOp::CeilReg(..) => "CEIL",
SsaOp::RoundReg(..) => "ROUND",
SsaOp::SinReg(..) => "SIN",
SsaOp::CosReg(..) => "COS",
SsaOp::TanReg(..) => "TAN",
Expand All @@ -300,11 +314,13 @@ impl SsaTape {
| SsaOp::MaxRegReg(out, lhs, rhs)
| SsaOp::ModRegReg(out, lhs, rhs)
| SsaOp::AndRegReg(out, lhs, rhs)
| SsaOp::AtanRegReg(out, lhs, rhs)
| SsaOp::OrRegReg(out, lhs, rhs) => {
let op = match op {
SsaOp::AddRegReg(..) => "ADD",
SsaOp::MulRegReg(..) => "MUL",
SsaOp::DivRegReg(..) => "DIV",
SsaOp::AtanRegReg(..) => "ATAN",
SsaOp::SubRegReg(..) => "SUB",
SsaOp::MinRegReg(..) => "MIN",
SsaOp::MaxRegReg(..) => "MAX",
Expand All @@ -322,6 +338,8 @@ impl SsaTape {
| SsaOp::DivImmReg(out, arg, imm)
| SsaOp::SubImmReg(out, arg, imm)
| SsaOp::SubRegImm(out, arg, imm)
| SsaOp::AtanRegImm(out, arg, imm)
| SsaOp::AtanImmReg(out, arg, imm)
| SsaOp::MinRegImm(out, arg, imm)
| SsaOp::MaxRegImm(out, arg, imm)
| SsaOp::ModRegImm(out, arg, imm)
Expand All @@ -335,6 +353,8 @@ impl SsaTape {
SsaOp::DivRegImm(..) => ("DIV", false),
SsaOp::SubImmReg(..) => ("SUB", true),
SsaOp::SubRegImm(..) => ("SUB", false),
SsaOp::AtanImmReg(..) => ("ATAN", true),
SsaOp::AtanRegImm(..) => ("ATAN", false),
SsaOp::MinRegImm(..) => ("MIN", false),
SsaOp::MaxRegImm(..) => ("MAX", false),
SsaOp::ModRegImm(..) => ("MOD", false),
Expand Down
78 changes: 76 additions & 2 deletions fidget/src/core/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -549,6 +549,49 @@ impl Context {
self.op_unary(a, UnaryOpcode::Square)
}

/// Builds a node which takes the floor of its input
/// ```
/// # let mut ctx = fidget::context::Context::new();
/// let x = ctx.x();
/// let op = ctx.floor(x).unwrap();
/// let v = ctx.eval_xyz(op, 1.2, 0.0, 0.0).unwrap();
/// assert_eq!(v, 1.0);
/// ```
pub fn floor<A: IntoNode>(&mut self, a: A) -> Result<Node, Error> {
let a = a.into_node(self)?;
self.op_unary(a, UnaryOpcode::Floor)
}

/// Builds a node which takes the ceiling of its input
/// ```
/// # let mut ctx = fidget::context::Context::new();
/// let x = ctx.x();
/// let op = ctx.ceil(x).unwrap();
/// let v = ctx.eval_xyz(op, 1.2, 0.0, 0.0).unwrap();
/// assert_eq!(v, 2.0);
/// ```
pub fn ceil<A: IntoNode>(&mut self, a: A) -> Result<Node, Error> {
let a = a.into_node(self)?;
self.op_unary(a, UnaryOpcode::Ceil)
}

/// Builds a node which rounds its input to the nearest integer
/// ```
/// # let mut ctx = fidget::context::Context::new();
/// let x = ctx.x();
/// let op = ctx.round(x).unwrap();
/// let v = ctx.eval_xyz(op, 1.2, 0.0, 0.0).unwrap();
/// assert_eq!(v, 1.0);
/// let v = ctx.eval_xyz(op, 1.6, 0.0, 0.0).unwrap();
/// assert_eq!(v, 2.0);
/// let v = ctx.eval_xyz(op, 1.5, 0.0, 0.0).unwrap();
/// assert_eq!(v, 2.0); // rounds away from 0.0 if ambiguous
/// ```
pub fn round<A: IntoNode>(&mut self, a: A) -> Result<Node, Error> {
let a = a.into_node(self)?;
self.op_unary(a, UnaryOpcode::Round)
}

/// Builds a node which performs subtraction.
/// ```
/// # let mut ctx = fidget::context::Context::new();
Expand All @@ -573,8 +616,7 @@ impl Context {
}
}

/// Builds a node which performs division. Under the hood, `a / b` is
/// converted into `a * (1 / b)`.
/// Builds a node which performs division.
/// ```
/// # let mut ctx = fidget::context::Context::new();
/// let x = ctx.x();
Expand All @@ -598,6 +640,26 @@ impl Context {
}
}

/// Builds a node which computes `atan2(y, x)`
/// ```
/// # let mut ctx = fidget::context::Context::new();
/// let x = ctx.x();
/// let y = ctx.y();
/// let op = ctx.atan2(y, x).unwrap();
/// let v = ctx.eval_xyz(op, 0.0, 1.0, 0.0).unwrap();
/// assert_eq!(v, std::f64::consts::FRAC_PI_2);
/// ```
pub fn atan2<A: IntoNode, B: IntoNode>(
&mut self,
y: A,
x: B,
) -> Result<Node, Error> {
let y = y.into_node(self)?;
let x = x.into_node(self)?;

self.op_binary(y, x, BinaryOpcode::Atan)
}

/// Builds a node that compares two values
///
/// The result is -1 if `a < b`, +1 if `a > b`, 0 if `a == b`, and `NaN` if
Expand Down Expand Up @@ -738,6 +800,7 @@ impl Context {
BinaryOpcode::Sub => a - b,
BinaryOpcode::Mul => a * b,
BinaryOpcode::Div => a / b,
BinaryOpcode::Atan => a.atan2(b),
BinaryOpcode::Min => a.min(b),
BinaryOpcode::Max => a.max(b),
BinaryOpcode::Compare => a
Expand Down Expand Up @@ -771,6 +834,9 @@ impl Context {
UnaryOpcode::Recip => 1.0 / a,
UnaryOpcode::Sqrt => a.sqrt(),
UnaryOpcode::Square => a * a,
UnaryOpcode::Floor => a.floor(),
UnaryOpcode::Ceil => a.ceil(),
UnaryOpcode::Round => a.round(),
UnaryOpcode::Sin => a.sin(),
UnaryOpcode::Cos => a.cos(),
UnaryOpcode::Tan => a.tan(),
Expand Down Expand Up @@ -837,6 +903,9 @@ impl Context {
"neg" => ctx.neg(pop()?)?,
"sqrt" => ctx.sqrt(pop()?)?,
"square" => ctx.square(pop()?)?,
"floor" => ctx.floor(pop()?)?,
"ceil" => ctx.ceil(pop()?)?,
"round" => ctx.round(pop()?)?,
"sin" => ctx.sin(pop()?)?,
"cos" => ctx.cos(pop()?)?,
"tan" => ctx.tan(pop()?)?,
Expand All @@ -851,6 +920,7 @@ impl Context {
"min" => ctx.min(pop()?, pop()?)?,
"max" => ctx.max(pop()?, pop()?)?,
"div" => ctx.div(pop()?, pop()?)?,
"atan2" => ctx.atan2(pop()?, pop()?)?,
"sub" => ctx.sub(pop()?, pop()?)?,
"compare" => ctx.compare(pop()?, pop()?)?,
"mod" => ctx.modulo(pop()?, pop()?)?,
Expand Down Expand Up @@ -897,6 +967,7 @@ impl Context {
BinaryOpcode::Sub => out += "sub",
BinaryOpcode::Mul => out += "mul",
BinaryOpcode::Div => out += "div",
BinaryOpcode::Atan => out += "atan2",
BinaryOpcode::Min => out += "min",
BinaryOpcode::Max => out += "max",
BinaryOpcode::Compare => out += "compare",
Expand All @@ -910,6 +981,9 @@ impl Context {
UnaryOpcode::Recip => out += "recip",
UnaryOpcode::Sqrt => out += "sqrt",
UnaryOpcode::Square => out += "square",
UnaryOpcode::Floor => out += "floor",
UnaryOpcode::Ceil => out += "ceil",
UnaryOpcode::Round => out += "round",
UnaryOpcode::Sin => out += "sin",
UnaryOpcode::Cos => out += "cos",
UnaryOpcode::Tan => out += "tan",
Expand Down
4 changes: 4 additions & 0 deletions fidget/src/core/context/op.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ pub enum UnaryOpcode {
Recip,
Sqrt,
Square,
Floor,
Ceil,
Round,
Sin,
Cos,
Tan,
Expand All @@ -29,6 +32,7 @@ pub enum BinaryOpcode {
Sub,
Mul,
Div,
Atan,
Min,
Max,
Compare,
Expand Down
12 changes: 12 additions & 0 deletions fidget/src/core/context/tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,15 @@ impl Tree {
pub fn square(&self) -> Self {
Self::op_unary(self.clone(), UnaryOpcode::Square)
}
pub fn floor(&self) -> Self {
Self::op_unary(self.clone(), UnaryOpcode::Floor)
}
pub fn ceil(&self) -> Self {
Self::op_unary(self.clone(), UnaryOpcode::Ceil)
}
pub fn round(&self) -> Self {
Self::op_unary(self.clone(), UnaryOpcode::Round)
}
pub fn sqrt(&self) -> Self {
Self::op_unary(self.clone(), UnaryOpcode::Sqrt)
}
Expand All @@ -193,6 +202,9 @@ impl Tree {
pub fn or<T: Into<Tree>>(&self, other: T) -> Self {
Self::op_binary(self.clone(), other.into(), BinaryOpcode::Or)
}
pub fn atan2<T: Into<Tree>>(&self, other: T) -> Self {
Self::op_binary(self.clone(), other.into(), BinaryOpcode::Atan)
}
pub fn neg(&self) -> Self {
Self::op_unary(self.clone(), UnaryOpcode::Neg)
}
Expand Down
Loading

0 comments on commit 4cc5f6a

Please sign in to comment.