@@ 45,6 45,17 @@ impl CContext {
b.set_version(1, 0);
b.capability(spirv::Capability::Shader);
b.memory_model(spirv::AddressingModel::Logical, spirv::MemoryModel::Simple);
+ /*
+ TODO: This is more trouble than it's worth right now!
+ My poor unit tests. ;_;
+ let filename_word = b.string("unknownnfile.cm");
+ b.source(
+ spirv::SourceLanguage::Unknown,
+ 1,
+ Some(filename_word),
+ Some("TODO: contents of unknownfile.cm"),
+ );
+ */
let typetable = FnvHashMap::default();
let consts = FnvHashMap::default();
// Global symbol table for functions, consts, etc...
@@ 344,7 355,58 @@ impl CContext {
let (w, t) = self.compile_expr(e, ctx)?;
self.compile_inferred_uniop(*op, t, w)
}
- ast::Expr::If(_cond, _ifpart, _elsepart) => unimplemented!(),
+ ast::Expr::If(cond, ifpart, elsepart) => {
+ // Okay, it looks like we just do:
+ // %condition = ...
+ // OpSelectionMerge %endlabel None
+ // OpBranchConditional %condition %iflabel %elselabel
+ // %iflabel = OpLabel
+ // ...
+ // %ifvalue = ...
+ // OpBranch %endlabel
+ // %elselabel = OpLabel
+ // ...
+ // %elsevalue = ...
+ // OpBranch %endlabel
+ // %endlabel = OpLabel
+ // %result = OpPhi %resulttype %ifvalue %iflabel %elsevalue %elselabel
+ let iflabel_word = self.b.id();
+ let elselabel_word = self.b.id();
+ let endlabel_word = self.b.id();
+
+ // Header
+ let (cond_word, _cond_type) = self.compile_expr(cond, ctx)?;
+ self.b
+ .selection_merge(endlabel_word, spirv::SelectionControl::NONE)?;
+ self.b
+ .branch_conditional(cond_word, iflabel_word, elselabel_word, [])?;
+
+ // If part
+ self.b.begin_basic_block(Some(iflabel_word))?;
+ // The turning into a block and to_vec() is a little silly, but effective.
+ let (ifvalue_word, iftype_word) =
+ self.compile_expr(&ast::Expr::Block(ifpart.to_vec()), ctx)?;
+ self.b.branch(endlabel_word)?;
+
+ // Else part
+ self.b.begin_basic_block(Some(elselabel_word))?;
+ let (elsevalue_word, elsetype_word) =
+ self.compile_expr(&ast::Expr::Block(elsepart.to_vec()), ctx)?;
+ self.b.branch(endlabel_word)?;
+
+ // Merge block
+ self.b.begin_basic_block(Some(endlabel_word))?;
+ assert_eq!(iftype_word, elsetype_word);
+ let res_word = self.b.phi(
+ iftype_word,
+ None,
+ [
+ (ifvalue_word, iflabel_word),
+ (elsevalue_word, elselabel_word),
+ ],
+ )?;
+ (res_word, iftype_word)
+ }
ast::Expr::Structure(_name, _vals) => {
// Okay I'm going to put this off until I can handle struct layout
// stuff.
@@ 612,9 674,8 @@ pub fn compile(ctx: &verify::VContext) -
}
cc.mongle_entry_points(ctx)?;
// TODO:
- // OpSource
- // OpName, at least for functions
+ // OpSource, though it will heck up the unit tests narsty
+ // OpName for types?
// OpMemberName for struct's
- // OpCapability
Ok(cc)
}
@@ 1,3 1,7 @@
+// TODO: Uniops
+// Test each side of if arms are the same
+// test each side of bin ops are the same
+// test types for if's
use crate::ast::{self, *};
use crate::compile;
use crate::verify;
@@ 25,6 29,8 @@ fn validate_module_header(asm: &str) ->
assert!(lines.next().unwrap().contains("OpEntryPoint Vertex"));
assert!(lines.next().unwrap().contains("OpEntryPoint Fragment"));
assert!(lines.next().unwrap().contains("OpExecutionMode"));
+ //assert!(lines.next().unwrap().contains("OpString"));
+ //assert!(lines.next().unwrap().contains("OpSource"));
// Skip OpName debug-info lines
let mut p = lines.peekable();
while p.peek().unwrap().contains("OpName ") {
@@ 542,3 548,82 @@ OpFunctionEnd"#,
);
spirv_val(&m);
}
+
+#[test]
+fn test_compile_if() {
+ let vert = Decl::Function(FunctionDecl {
+ name: String::from("vertex"),
+ params: vec![Param {
+ name: "i".into(),
+ typ: Type("F32".into()),
+ }],
+ returns: Type("F32".into()),
+ body: vec![Expr::Var("i".into())],
+ });
+ let frag = ast::Decl::Function(ast::FunctionDecl {
+ name: String::from("fragment"),
+ params: vec![ast::Param {
+ name: "input".into(),
+ typ: ast::Type("F32".into()),
+ }],
+ returns: ast::Type("F32".into()),
+ body: vec![Expr::If(
+ Box::new(Expr::Literal(Lit::Bool(true))),
+ vec![Expr::BinOp(
+ Op::Add,
+ Box::new(Expr::Literal(Lit::F32(5.0))),
+ Box::new(Expr::Var("input".into())),
+ )],
+ vec![Expr::BinOp(
+ Op::Mul,
+ Box::new(Expr::Literal(Lit::F32(50.0))),
+ Box::new(Expr::Var("input".into())),
+ )],
+ )],
+ });
+ let program = vec![vert, frag];
+ let ctx = verify::verify(program).unwrap();
+ let c = compile::compile(&ctx).unwrap();
+
+ let m = c.b.module();
+ let dis = m.disassemble();
+ println!("{}", dis);
+ let rest = validate_module_header(&dis);
+ spirv_val(&m);
+ validate_lines(
+ rest,
+ r#"%15 = OpConstantTrue %4
+%16 = OpConstant %1 5.0
+%18 = OpConstant %1 50.0
+%21 = OpTypeFunction %5
+%6 = OpFunction %1 DontInline|Pure|Const %3
+%7 = OpFunctionParameter %1
+%8 = OpLabel
+OpReturnValue %7
+OpFunctionEnd
+%9 = OpFunction %1 DontInline|Pure|Const %3
+%10 = OpFunctionParameter %1
+%11 = OpLabel
+OpSelectionMerge %14 None
+OpBranchConditional %15 %12 %13
+%12 = OpLabel
+%17 = OpFAdd %1 %16 %10
+OpBranch %14
+%13 = OpLabel
+%19 = OpFMul %1 %18 %10
+OpBranch %14
+%14 = OpLabel
+%20 = OpPhi %1 %17 %12 %19 %13
+OpReturnValue %20
+OpFunctionEnd
+%22 = OpFunction %5 DontInline|Const %21
+%23 = OpLabel
+OpReturn
+OpFunctionEnd
+%24 = OpFunction %5 DontInline|Const %21
+%25 = OpLabel
+OpReturn
+OpFunctionEnd"#,
+ );
+ spirv_val(&m);
+}