Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support RETURN keyword #234

Merged
merged 11 commits into from
Aug 12, 2021
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/target
**/*.rs.bk
**/*.un~
**/*.toml~
**/*.toml~
*.ir
*.o
9 changes: 9 additions & 0 deletions examples/function_with_return.st
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FUNCTION smaller_than_ten: INT
VAR_INPUT
n : SINT;
END_VAR

IF n < 10 THEN
RETURN;
END_IF;
END_FUNCTION
5 changes: 5 additions & 0 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -499,6 +499,9 @@ pub enum Statement {
CaseCondition {
condition: Box<Statement>,
},
ReturnStatement {
location: SourceRange,
},
}

impl Debug for Statement {
Expand Down Expand Up @@ -705,6 +708,7 @@ impl Debug for Statement {
.debug_struct("CaseCondition")
.field("condition", condition)
.finish(),
Statement::ReturnStatement { .. } => f.debug_struct("ReturnStatement").finish(),
}
}
}
Expand Down Expand Up @@ -783,6 +787,7 @@ impl Statement {
}
Statement::MultipliedStatement { location, .. } => location.clone(),
Statement::CaseCondition { condition } => condition.get_location(),
Statement::ReturnStatement { location } => location.clone(),
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion src/codegen/generators/pou_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,7 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> {
let statement_gen = StatementCodeGenerator::new(
&self.llvm,
self.index,
self,
&local_index,
&function_context,
);
Expand Down Expand Up @@ -254,7 +255,7 @@ impl<'ink, 'cg> PouGenerator<'ink, 'cg> {
/// generates the function's return statement only if the given pou_type is a `PouType::Function`
///
/// a function returns the value of the local variable that has the function's name
fn generate_return_statement(
pub fn generate_return_statement(
&self,
function_context: &FunctionContext<'ink>,
local_index: &LlvmTypedIndex<'ink>,
Expand Down
13 changes: 13 additions & 0 deletions src/codegen/generators/statement_generator.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
// Copyright (c) 2020 Ghaith Hachem and Mathias Rieder
use std::ops::Range;

use super::pou_generator::PouGenerator;
use super::{expression_generator::ExpressionCodeGenerator, llvm::Llvm};
use crate::ast::PouType;
use crate::codegen::LlvmTypedIndex;
use crate::typesystem::{RANGE_CHECK_LS_FN, RANGE_CHECK_LU_FN, RANGE_CHECK_S_FN, RANGE_CHECK_U_FN};
use crate::{ast::SourceRange, codegen::llvm_typesystem::cast_if_needed};
Expand Down Expand Up @@ -31,6 +33,7 @@ pub struct FunctionContext<'a> {
pub struct StatementCodeGenerator<'a, 'b> {
llvm: &'b Llvm<'a>,
index: &'b Index,
pou_generator: &'b PouGenerator<'a, 'b>,
llvm_index: &'b LlvmTypedIndex<'a>,
function_context: &'b FunctionContext<'a>,

Expand All @@ -43,12 +46,14 @@ impl<'a, 'b> StatementCodeGenerator<'a, 'b> {
pub fn new(
llvm: &'b Llvm<'a>,
index: &'b Index,
pou_generator: &'b PouGenerator<'a, 'b>,
llvm_index: &'b LlvmTypedIndex<'a>,
linking_context: &'b FunctionContext<'a>,
) -> StatementCodeGenerator<'a, 'b> {
StatementCodeGenerator {
llvm,
index,
pou_generator,
llvm_index,
function_context: linking_context,
load_prefix: "load_".to_string(),
Expand Down Expand Up @@ -119,6 +124,14 @@ impl<'a, 'b> StatementCodeGenerator<'a, 'b> {
} => {
self.generate_case_statement(selector, case_blocks, else_block)?;
}
Statement::ReturnStatement { location } => {
self.pou_generator.generate_return_statement(
self.function_context,
self.llvm_index,
PouType::Function,
Some(location.clone()),
)?;
}
_ => {
self.create_expr_generator()
.generate_expression(statement)?;
Expand Down
41 changes: 41 additions & 0 deletions src/codegen/tests/code_gen_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3797,6 +3797,47 @@ fn nested_array_access() {
assert_eq!(result, expected);
}

#[test]
fn returning_early_in_function() {
let result = codegen!(
"
FUNCTION smaller_than_ten: INT
VAR_INPUT n : SINT; END_VAR
IF n < 10 THEN
RETURN;
END_IF;
END_FUNCTION
"
);

let expected = r#"; ModuleID = 'main'
source_filename = "main"

%smaller_than_ten_interface = type { i8 }

define i16 @smaller_than_ten(%smaller_than_ten_interface* %0) {
entry:
%n = getelementptr inbounds %smaller_than_ten_interface, %smaller_than_ten_interface* %0, i32 0, i32 0
%smaller_than_ten = alloca i16, align 2
%load_n = load i8, i8* %n, align 1
%1 = sext i8 %load_n to i32
%tmpVar = icmp slt i32 %1, 10
br i1 %tmpVar, label %condition_body, label %continue

condition_body: ; preds = %entry
%smaller_than_ten_ret = load i16, i16* %smaller_than_ten, align 2
ret i16 %smaller_than_ten_ret
br label %continue

continue: ; preds = %condition_body, %entry
%smaller_than_ten_ret1 = load i16, i16* %smaller_than_ten, align 2
ret i16 %smaller_than_ten_ret1
}
"#;

assert_eq!(result, expected);
}

#[test]
fn accessing_nested_array_in_struct() {
let result = codegen!(
Expand Down
3 changes: 3 additions & 0 deletions src/lexer/tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,9 @@ pub enum Token {
#[token("CASE")]
KeywordCase,

#[token("RETURN")]
KeywordReturn,

#[token("ARRAY")]
KeywordArray,

Expand Down
7 changes: 7 additions & 0 deletions src/parser/control_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,17 @@ pub fn parse_control_statement(lexer: &mut ParseSession) -> Statement {
KeywordWhile => parse_while_statement(lexer),
KeywordRepeat => parse_repeat_statement(lexer),
KeywordCase => parse_case_statement(lexer),
KeywordReturn => parse_return_statement(lexer),
_ => parse_statement(lexer),
}
}

fn parse_return_statement(lexer: &mut ParseSession) -> Statement {
let location = lexer.location();
lexer.advance();
Statement::ReturnStatement { location }
}

fn parse_if_statement(lexer: &mut ParseSession) -> Statement {
let start = lexer.range().start;
lexer.advance(); //If
Expand Down
10 changes: 10 additions & 0 deletions src/parser/tests/control_parser_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ fn if_statement() {
assert_eq!(ast_string, expected_ast);
}

#[test]
fn test_return_statement() {
let lexer = super::lex("PROGRAM ret RETURN END_PROGRAM");
let result = parse(lexer).0;
let prg = &result.implementations[0];
let stmt = &prg.statements[0];

assert_eq!(format!("{:?}", stmt), "ReturnStatement");
}

#[test]
fn if_else_statement_with_expressions() {
let lexer = super::lex(
Expand Down