mirror of
				https://github.com/YosysHQ/yosys
				synced 2025-10-31 03:32:29 +00:00 
			
		
		
		
	Merge pull request #2573 from zachjs/repeat-call
verilog: refactored constant function evaluation
This commit is contained in:
		
						commit
						326f1c9db4
					
				
					 4 changed files with 176 additions and 72 deletions
				
			
		|  | @ -264,10 +264,9 @@ namespace AST | ||||||
| 
 | 
 | ||||||
| 		// additional functionality for evaluating constant functions
 | 		// additional functionality for evaluating constant functions
 | ||||||
| 		struct varinfo_t { RTLIL::Const val; int offset; bool is_signed; }; | 		struct varinfo_t { RTLIL::Const val; int offset; bool is_signed; }; | ||||||
| 		bool has_const_only_constructs(bool &recommend_const_eval); | 		bool has_const_only_constructs(); | ||||||
| 		bool has_const_only_constructs(std::set<std::string>& visited, bool &recommend_const_eval); | 		bool replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall, bool must_succeed); | ||||||
| 		void replace_variables(std::map<std::string, varinfo_t> &variables, AstNode *fcall); | 		AstNode *eval_const_function(AstNode *fcall, bool must_succeed); | ||||||
| 		AstNode *eval_const_function(AstNode *fcall); |  | ||||||
| 		bool is_simple_const_expr(); | 		bool is_simple_const_expr(); | ||||||
| 		std::string process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint); | 		std::string process_format_str(const std::string &sformat, int next_arg, int stage, int width_hint, bool sign_hint); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -1218,11 +1218,6 @@ bool AstNode::simplify(bool const_fold, bool at_zero, bool in_lvalue, int stage, | ||||||
| 				current_block = this; | 				current_block = this; | ||||||
| 				current_block_child = children[i]; | 				current_block_child = children[i]; | ||||||
| 			} | 			} | ||||||
| 			if (!in_param_here && type == AST_FCALL) { |  | ||||||
| 				bool recommend_const_eval = false; |  | ||||||
| 				bool require_const_eval = has_const_only_constructs(recommend_const_eval); |  | ||||||
| 				in_param_here = recommend_const_eval || require_const_eval; |  | ||||||
| 			} |  | ||||||
| 			if ((type == AST_ALWAYS || type == AST_INITIAL) && children[i]->type == AST_BLOCK) | 			if ((type == AST_ALWAYS || type == AST_INITIAL) && children[i]->type == AST_BLOCK) | ||||||
| 				current_top_block = children[i]; | 				current_top_block = children[i]; | ||||||
| 			if (i == 0 && child_0_is_self_determined) | 			if (i == 0 && child_0_is_self_determined) | ||||||
|  | @ -3186,10 +3181,9 @@ skip_dynamic_range_lvalue_expansion:; | ||||||
| 		decl->replace_result_wire_name_in_function(str, "$result"); // enables recursion
 | 		decl->replace_result_wire_name_in_function(str, "$result"); // enables recursion
 | ||||||
| 		decl->expand_genblock(prefix); | 		decl->expand_genblock(prefix); | ||||||
| 
 | 
 | ||||||
| 		bool recommend_const_eval = false; | 		if (decl->type == AST_FUNCTION && !decl->attributes.count(ID::via_celltype)) | ||||||
| 		bool require_const_eval = in_param ? false : has_const_only_constructs(recommend_const_eval); |  | ||||||
| 		if ((in_param || recommend_const_eval || require_const_eval) && !decl->attributes.count(ID::via_celltype)) |  | ||||||
| 		{ | 		{ | ||||||
|  | 			bool require_const_eval = decl->has_const_only_constructs(); | ||||||
| 			bool all_args_const = true; | 			bool all_args_const = true; | ||||||
| 			for (auto child : children) { | 			for (auto child : children) { | ||||||
| 				while (child->simplify(true, false, false, 1, -1, false, true)) { } | 				while (child->simplify(true, false, false, 1, -1, false, true)) { } | ||||||
|  | @ -3200,11 +3194,13 @@ skip_dynamic_range_lvalue_expansion:; | ||||||
| 			if (all_args_const) { | 			if (all_args_const) { | ||||||
| 				AstNode *func_workspace = decl->clone(); | 				AstNode *func_workspace = decl->clone(); | ||||||
| 				func_workspace->str = prefix_id(prefix, "$result"); | 				func_workspace->str = prefix_id(prefix, "$result"); | ||||||
| 				newNode = func_workspace->eval_const_function(this); | 				newNode = func_workspace->eval_const_function(this, in_param || require_const_eval); | ||||||
| 				delete func_workspace; | 				delete func_workspace; | ||||||
|  | 				if (newNode) { | ||||||
| 					delete decl; | 					delete decl; | ||||||
| 					goto apply_newNode; | 					goto apply_newNode; | ||||||
| 				} | 				} | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 			if (in_param) | 			if (in_param) | ||||||
| 				log_file_error(filename, location.first_line, "Non-constant function call in constant expression.\n"); | 				log_file_error(filename, location.first_line, "Non-constant function call in constant expression.\n"); | ||||||
|  | @ -4502,33 +4498,12 @@ bool AstNode::detect_latch(const std::string &var) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| bool AstNode::has_const_only_constructs(bool &recommend_const_eval) | bool AstNode::has_const_only_constructs() | ||||||
| { | { | ||||||
| 	std::set<std::string> visited; |  | ||||||
| 	return has_const_only_constructs(visited, recommend_const_eval); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| bool AstNode::has_const_only_constructs(std::set<std::string>& visited, bool &recommend_const_eval) |  | ||||||
| { |  | ||||||
| 	if (type == AST_FUNCTION || type == AST_TASK) |  | ||||||
| 	{ |  | ||||||
| 		if (visited.count(str)) |  | ||||||
| 		{ |  | ||||||
| 			recommend_const_eval = true; |  | ||||||
| 			return false; |  | ||||||
| 		} |  | ||||||
| 		visited.insert(str); |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	if (type == AST_FOR) |  | ||||||
| 		recommend_const_eval = true; |  | ||||||
| 	if (type == AST_WHILE || type == AST_REPEAT) | 	if (type == AST_WHILE || type == AST_REPEAT) | ||||||
| 		return true; | 		return true; | ||||||
| 	if (type == AST_FCALL && current_scope.count(str)) |  | ||||||
| 		if (current_scope[str]->has_const_only_constructs(visited, recommend_const_eval)) |  | ||||||
| 			return true; |  | ||||||
| 	for (auto child : children) | 	for (auto child : children) | ||||||
| 		if (child->AstNode::has_const_only_constructs(visited, recommend_const_eval)) | 		if (child->has_const_only_constructs()) | ||||||
| 			return true; | 			return true; | ||||||
| 	return false; | 	return false; | ||||||
| } | } | ||||||
|  | @ -4544,19 +4519,26 @@ bool AstNode::is_simple_const_expr() | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // helper function for AstNode::eval_const_function()
 | // helper function for AstNode::eval_const_function()
 | ||||||
| void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall) | bool AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &variables, AstNode *fcall, bool must_succeed) | ||||||
| { | { | ||||||
| 	if (type == AST_IDENTIFIER && variables.count(str)) { | 	if (type == AST_IDENTIFIER && variables.count(str)) { | ||||||
| 		int offset = variables.at(str).offset, width = variables.at(str).val.bits.size(); | 		int offset = variables.at(str).offset, width = variables.at(str).val.bits.size(); | ||||||
| 		if (!children.empty()) { | 		if (!children.empty()) { | ||||||
| 			if (children.size() != 1 || children.at(0)->type != AST_RANGE) | 			if (children.size() != 1 || children.at(0)->type != AST_RANGE) { | ||||||
|  | 				if (!must_succeed) | ||||||
|  | 					return false; | ||||||
| 				log_file_error(filename, location.first_line, "Memory access in constant function is not supported\n%s:%d.%d-%d.%d: ...called from here.\n", | 				log_file_error(filename, location.first_line, "Memory access in constant function is not supported\n%s:%d.%d-%d.%d: ...called from here.\n", | ||||||
| 						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | 						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | ||||||
| 			children.at(0)->replace_variables(variables, fcall); | 			} | ||||||
|  | 			if (!children.at(0)->replace_variables(variables, fcall, must_succeed)) | ||||||
|  | 				return false; | ||||||
| 			while (simplify(true, false, false, 1, -1, false, true)) { } | 			while (simplify(true, false, false, 1, -1, false, true)) { } | ||||||
| 			if (!children.at(0)->range_valid) | 			if (!children.at(0)->range_valid) { | ||||||
|  | 				if (!must_succeed) | ||||||
|  | 					return false; | ||||||
| 				log_file_error(filename, location.first_line, "Non-constant range\n%s:%d.%d-%d.%d: ... called from here.\n", | 				log_file_error(filename, location.first_line, "Non-constant range\n%s:%d.%d-%d.%d: ... called from here.\n", | ||||||
| 						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | 						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | ||||||
|  | 			} | ||||||
| 			offset = min(children.at(0)->range_left, children.at(0)->range_right); | 			offset = min(children.at(0)->range_left, children.at(0)->range_right); | ||||||
| 			width = min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width); | 			width = min(std::abs(children.at(0)->range_left - children.at(0)->range_right) + 1, width); | ||||||
| 		} | 		} | ||||||
|  | @ -4566,19 +4548,22 @@ void AstNode::replace_variables(std::map<std::string, AstNode::varinfo_t> &varia | ||||||
| 		AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed); | 		AstNode *newNode = mkconst_bits(new_bits, variables.at(str).is_signed); | ||||||
| 		newNode->cloneInto(this); | 		newNode->cloneInto(this); | ||||||
| 		delete newNode; | 		delete newNode; | ||||||
| 		return; | 		return true; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	for (auto &child : children) | 	for (auto &child : children) | ||||||
| 		child->replace_variables(variables, fcall); | 		if (!child->replace_variables(variables, fcall, must_succeed)) | ||||||
|  | 			return false; | ||||||
|  | 	return true; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // evaluate functions with all-const arguments
 | // attempt to statically evaluate a functions with all-const arguments
 | ||||||
| AstNode *AstNode::eval_const_function(AstNode *fcall) | AstNode *AstNode::eval_const_function(AstNode *fcall, bool must_succeed) | ||||||
| { | { | ||||||
| 	std::map<std::string, AstNode*> backup_scope; | 	std::map<std::string, AstNode*> backup_scope = current_scope; | ||||||
| 	std::map<std::string, AstNode::varinfo_t> variables; | 	std::map<std::string, AstNode::varinfo_t> variables; | ||||||
| 	AstNode *block = new AstNode(AST_BLOCK); | 	AstNode *block = new AstNode(AST_BLOCK); | ||||||
|  | 	AstNode *result = nullptr; | ||||||
| 
 | 
 | ||||||
| 	size_t argidx = 0; | 	size_t argidx = 0; | ||||||
| 	for (auto child : children) | 	for (auto child : children) | ||||||
|  | @ -4600,9 +4585,12 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) | ||||||
| 		if (stmt->type == AST_WIRE) | 		if (stmt->type == AST_WIRE) | ||||||
| 		{ | 		{ | ||||||
| 			while (stmt->simplify(true, false, false, 1, -1, false, true)) { } | 			while (stmt->simplify(true, false, false, 1, -1, false, true)) { } | ||||||
| 			if (!stmt->range_valid) | 			if (!stmt->range_valid) { | ||||||
|  | 				if (!must_succeed) | ||||||
|  | 					goto finished; | ||||||
| 				log_file_error(stmt->filename, stmt->location.first_line, "Can't determine size of variable %s\n%s:%d.%d-%d.%d: ... called from here.\n", | 				log_file_error(stmt->filename, stmt->location.first_line, "Can't determine size of variable %s\n%s:%d.%d-%d.%d: ... called from here.\n", | ||||||
| 						stmt->str.c_str(), fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | 						stmt->str.c_str(), fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | ||||||
|  | 			} | ||||||
| 			variables[stmt->str].val = RTLIL::Const(RTLIL::State::Sx, abs(stmt->range_left - stmt->range_right)+1); | 			variables[stmt->str].val = RTLIL::Const(RTLIL::State::Sx, abs(stmt->range_left - stmt->range_right)+1); | ||||||
| 			variables[stmt->str].offset = min(stmt->range_left, stmt->range_right); | 			variables[stmt->str].offset = min(stmt->range_left, stmt->range_right); | ||||||
| 			variables[stmt->str].is_signed = stmt->is_signed; | 			variables[stmt->str].is_signed = stmt->is_signed; | ||||||
|  | @ -4616,8 +4604,6 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) | ||||||
| 					variables[stmt->str].val = arg_node->realAsConst(width); | 					variables[stmt->str].val = arg_node->realAsConst(width); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 			if (!backup_scope.count(stmt->str)) |  | ||||||
| 				backup_scope[stmt->str] = current_scope[stmt->str]; |  | ||||||
| 			current_scope[stmt->str] = stmt; | 			current_scope[stmt->str] = stmt; | ||||||
| 
 | 
 | ||||||
| 			block->children.erase(block->children.begin()); | 			block->children.erase(block->children.begin()); | ||||||
|  | @ -4630,8 +4616,6 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) | ||||||
| 		{ | 		{ | ||||||
| 			while (stmt->simplify(true, false, false, 1, -1, false, true)) { } | 			while (stmt->simplify(true, false, false, 1, -1, false, true)) { } | ||||||
| 
 | 
 | ||||||
| 			if (!backup_scope.count(stmt->str)) |  | ||||||
| 				backup_scope[stmt->str] = current_scope[stmt->str]; |  | ||||||
| 			current_scope[stmt->str] = stmt; | 			current_scope[stmt->str] = stmt; | ||||||
| 
 | 
 | ||||||
| 			block->children.erase(block->children.begin()); | 			block->children.erase(block->children.begin()); | ||||||
|  | @ -4642,32 +4626,46 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) | ||||||
| 		{ | 		{ | ||||||
| 			if (stmt->children.at(0)->type == AST_IDENTIFIER && stmt->children.at(0)->children.size() != 0 && | 			if (stmt->children.at(0)->type == AST_IDENTIFIER && stmt->children.at(0)->children.size() != 0 && | ||||||
| 					stmt->children.at(0)->children.at(0)->type == AST_RANGE) | 					stmt->children.at(0)->children.at(0)->type == AST_RANGE) | ||||||
| 				stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall); | 				if (!stmt->children.at(0)->children.at(0)->replace_variables(variables, fcall, must_succeed)) | ||||||
| 			stmt->children.at(1)->replace_variables(variables, fcall); | 					goto finished; | ||||||
|  | 			if (!stmt->children.at(1)->replace_variables(variables, fcall, must_succeed)) | ||||||
|  | 				goto finished; | ||||||
| 			while (stmt->simplify(true, false, false, 1, -1, false, true)) { } | 			while (stmt->simplify(true, false, false, 1, -1, false, true)) { } | ||||||
| 
 | 
 | ||||||
| 			if (stmt->type != AST_ASSIGN_EQ) | 			if (stmt->type != AST_ASSIGN_EQ) | ||||||
| 				continue; | 				continue; | ||||||
| 
 | 
 | ||||||
| 			if (stmt->children.at(1)->type != AST_CONSTANT) | 			if (stmt->children.at(1)->type != AST_CONSTANT) { | ||||||
|  | 				if (!must_succeed) | ||||||
|  | 					goto finished; | ||||||
| 				log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here. X\n", | 				log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here. X\n", | ||||||
| 						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | 						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 			if (stmt->children.at(0)->type != AST_IDENTIFIER) | 			if (stmt->children.at(0)->type != AST_IDENTIFIER) { | ||||||
|  | 				if (!must_succeed) | ||||||
|  | 					goto finished; | ||||||
| 				log_file_error(stmt->filename, stmt->location.first_line, "Unsupported composite left hand side in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", | 				log_file_error(stmt->filename, stmt->location.first_line, "Unsupported composite left hand side in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", | ||||||
| 						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | 						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 			if (!variables.count(stmt->children.at(0)->str)) | 			if (!variables.count(stmt->children.at(0)->str)) { | ||||||
|  | 				if (!must_succeed) | ||||||
|  | 					goto finished; | ||||||
| 				log_file_error(stmt->filename, stmt->location.first_line, "Assignment to non-local variable in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", | 				log_file_error(stmt->filename, stmt->location.first_line, "Assignment to non-local variable in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", | ||||||
| 						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | 						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 			if (stmt->children.at(0)->children.empty()) { | 			if (stmt->children.at(0)->children.empty()) { | ||||||
| 				variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.bits.size()); | 				variables[stmt->children.at(0)->str].val = stmt->children.at(1)->bitsAsConst(variables[stmt->children.at(0)->str].val.bits.size()); | ||||||
| 			} else { | 			} else { | ||||||
| 				AstNode *range = stmt->children.at(0)->children.at(0); | 				AstNode *range = stmt->children.at(0)->children.at(0); | ||||||
| 				if (!range->range_valid) | 				if (!range->range_valid) { | ||||||
|  | 					if (!must_succeed) | ||||||
|  | 						goto finished; | ||||||
| 					log_file_error(range->filename, range->location.first_line, "Non-constant range\n%s:%d.%d-%d.%d: ... called from here.\n", | 					log_file_error(range->filename, range->location.first_line, "Non-constant range\n%s:%d.%d-%d.%d: ... called from here.\n", | ||||||
| 							fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | 							fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | ||||||
|  | 				} | ||||||
| 				int offset = min(range->range_left, range->range_right); | 				int offset = min(range->range_left, range->range_right); | ||||||
| 				int width = std::abs(range->range_left - range->range_right) + 1; | 				int width = std::abs(range->range_left - range->range_right) + 1; | ||||||
| 				varinfo_t &v = variables[stmt->children.at(0)->str]; | 				varinfo_t &v = variables[stmt->children.at(0)->str]; | ||||||
|  | @ -4694,12 +4692,16 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) | ||||||
| 		if (stmt->type == AST_WHILE) | 		if (stmt->type == AST_WHILE) | ||||||
| 		{ | 		{ | ||||||
| 			AstNode *cond = stmt->children.at(0)->clone(); | 			AstNode *cond = stmt->children.at(0)->clone(); | ||||||
| 			cond->replace_variables(variables, fcall); | 			if (!cond->replace_variables(variables, fcall, must_succeed)) | ||||||
|  | 				goto finished; | ||||||
| 			while (cond->simplify(true, false, false, 1, -1, false, true)) { } | 			while (cond->simplify(true, false, false, 1, -1, false, true)) { } | ||||||
| 
 | 
 | ||||||
| 			if (cond->type != AST_CONSTANT) | 			if (cond->type != AST_CONSTANT) { | ||||||
|  | 				if (!must_succeed) | ||||||
|  | 					goto finished; | ||||||
| 				log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", | 				log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", | ||||||
| 						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | 						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 			if (cond->asBool()) { | 			if (cond->asBool()) { | ||||||
| 				block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); | 				block->children.insert(block->children.begin(), stmt->children.at(1)->clone()); | ||||||
|  | @ -4715,12 +4717,16 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) | ||||||
| 		if (stmt->type == AST_REPEAT) | 		if (stmt->type == AST_REPEAT) | ||||||
| 		{ | 		{ | ||||||
| 			AstNode *num = stmt->children.at(0)->clone(); | 			AstNode *num = stmt->children.at(0)->clone(); | ||||||
| 			num->replace_variables(variables, fcall); | 			if (!num->replace_variables(variables, fcall, must_succeed)) | ||||||
|  | 				goto finished; | ||||||
| 			while (num->simplify(true, false, false, 1, -1, false, true)) { } | 			while (num->simplify(true, false, false, 1, -1, false, true)) { } | ||||||
| 
 | 
 | ||||||
| 			if (num->type != AST_CONSTANT) | 			if (num->type != AST_CONSTANT) { | ||||||
|  | 				if (!must_succeed) | ||||||
|  | 					goto finished; | ||||||
| 				log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", | 				log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", | ||||||
| 						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | 						fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 			block->children.erase(block->children.begin()); | 			block->children.erase(block->children.begin()); | ||||||
| 			for (int i = 0; i < num->bitsAsConst().as_int(); i++) | 			for (int i = 0; i < num->bitsAsConst().as_int(); i++) | ||||||
|  | @ -4734,7 +4740,8 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) | ||||||
| 		if (stmt->type == AST_CASE) | 		if (stmt->type == AST_CASE) | ||||||
| 		{ | 		{ | ||||||
| 			AstNode *expr = stmt->children.at(0)->clone(); | 			AstNode *expr = stmt->children.at(0)->clone(); | ||||||
| 			expr->replace_variables(variables, fcall); | 			if (!expr->replace_variables(variables, fcall, must_succeed)) | ||||||
|  | 				goto finished; | ||||||
| 			while (expr->simplify(true, false, false, 1, -1, false, true)) { } | 			while (expr->simplify(true, false, false, 1, -1, false, true)) { } | ||||||
| 
 | 
 | ||||||
| 			AstNode *sel_case = NULL; | 			AstNode *sel_case = NULL; | ||||||
|  | @ -4751,14 +4758,18 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) | ||||||
| 				for (size_t j = 0; j+1 < stmt->children.at(i)->children.size() && !found_match; j++) | 				for (size_t j = 0; j+1 < stmt->children.at(i)->children.size() && !found_match; j++) | ||||||
| 				{ | 				{ | ||||||
| 					AstNode *cond = stmt->children.at(i)->children.at(j)->clone(); | 					AstNode *cond = stmt->children.at(i)->children.at(j)->clone(); | ||||||
| 					cond->replace_variables(variables, fcall); | 					if (!cond->replace_variables(variables, fcall, must_succeed)) | ||||||
|  | 						goto finished; | ||||||
| 
 | 
 | ||||||
| 					cond = new AstNode(AST_EQ, expr->clone(), cond); | 					cond = new AstNode(AST_EQ, expr->clone(), cond); | ||||||
| 					while (cond->simplify(true, false, false, 1, -1, false, true)) { } | 					while (cond->simplify(true, false, false, 1, -1, false, true)) { } | ||||||
| 
 | 
 | ||||||
| 					if (cond->type != AST_CONSTANT) | 					if (cond->type != AST_CONSTANT) { | ||||||
|  | 						if (!must_succeed) | ||||||
|  | 							goto finished; | ||||||
| 						log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", | 						log_file_error(stmt->filename, stmt->location.first_line, "Non-constant expression in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", | ||||||
| 								fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | 								fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | ||||||
|  | 					} | ||||||
| 
 | 
 | ||||||
| 					found_match = cond->asBool(); | 					found_match = cond->asBool(); | ||||||
| 					delete cond; | 					delete cond; | ||||||
|  | @ -4790,20 +4801,20 @@ AstNode *AstNode::eval_const_function(AstNode *fcall) | ||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		if (!must_succeed) | ||||||
|  | 			goto finished; | ||||||
| 		log_file_error(stmt->filename, stmt->location.first_line, "Unsupported language construct in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", | 		log_file_error(stmt->filename, stmt->location.first_line, "Unsupported language construct in constant function\n%s:%d.%d-%d.%d: ... called from here.\n", | ||||||
| 				fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | 				fcall->filename.c_str(), fcall->location.first_line, fcall->location.first_column, fcall->location.last_line, fcall->location.last_column); | ||||||
| 		log_abort(); | 		log_abort(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	result = AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); | ||||||
|  | 
 | ||||||
|  | finished: | ||||||
| 	delete block; | 	delete block; | ||||||
|  | 	current_scope = backup_scope; | ||||||
| 
 | 
 | ||||||
| 	for (auto &it : backup_scope) | 	return result; | ||||||
| 		if (it.second == NULL) |  | ||||||
| 			current_scope.erase(it.first); |  | ||||||
| 		else |  | ||||||
| 			current_scope[it.first] = it.second; |  | ||||||
| 
 |  | ||||||
| 	return AstNode::mkconst_bits(variables.at(str).val.bits, variables.at(str).is_signed); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void AstNode::allocateDefaultEnumValues() | void AstNode::allocateDefaultEnumValues() | ||||||
|  |  | ||||||
							
								
								
									
										61
									
								
								tests/simple/const_fold_func.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										61
									
								
								tests/simple/const_fold_func.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,61 @@ | ||||||
|  | module top( | ||||||
|  | 	input wire [3:0] inp, | ||||||
|  | 	output wire [3:0] out1, out2, out3, out4, out5, | ||||||
|  | 	output reg [3:0] out6 | ||||||
|  | ); | ||||||
|  | 	function automatic [3:0] flip; | ||||||
|  | 		input [3:0] inp; | ||||||
|  | 		flip = ~inp; | ||||||
|  | 	endfunction | ||||||
|  | 
 | ||||||
|  | 	function automatic [3:0] help; | ||||||
|  | 		input [3:0] inp; | ||||||
|  | 		help = flip(inp); | ||||||
|  | 	endfunction | ||||||
|  | 
 | ||||||
|  | 	// while loops are const-eval-only
 | ||||||
|  | 	function automatic [3:0] loop; | ||||||
|  | 		input [3:0] inp; | ||||||
|  | 		reg [3:0] val; | ||||||
|  | 		begin | ||||||
|  | 			val = inp; | ||||||
|  | 			loop = 1; | ||||||
|  | 			while (val != inp) begin | ||||||
|  | 				loop = loop * 2; | ||||||
|  | 				val = val + 1; | ||||||
|  | 			end | ||||||
|  | 		end | ||||||
|  | 	endfunction | ||||||
|  | 
 | ||||||
|  | 	// not const-eval-only, despite calling a const-eval-only function
 | ||||||
|  | 	function automatic [3:0] help_mul; | ||||||
|  | 		input [3:0] inp; | ||||||
|  | 		help_mul = inp * loop(2); | ||||||
|  | 	endfunction | ||||||
|  | 
 | ||||||
|  | 	// can be elaborated so long as exp is a constant
 | ||||||
|  | 	function automatic [3:0] pow_flip_a; | ||||||
|  | 		input [3:0] base, exp; | ||||||
|  | 		begin | ||||||
|  | 			pow_flip_a = 1; | ||||||
|  | 			if (exp > 0) | ||||||
|  | 				pow_flip_a = base * pow_flip_a(flip(base), exp - 1); | ||||||
|  | 		end | ||||||
|  | 	endfunction | ||||||
|  | 
 | ||||||
|  | 	function automatic [3:0] pow_flip_b; | ||||||
|  | 		input [3:0] base, exp; | ||||||
|  | 		begin | ||||||
|  | 			out6[exp] = base & 1; | ||||||
|  | 			pow_flip_b = 1; | ||||||
|  | 			if (exp > 0) | ||||||
|  | 				pow_flip_b = base * pow_flip_b(flip(base), exp - 1); | ||||||
|  | 		end | ||||||
|  | 	endfunction | ||||||
|  | 
 | ||||||
|  | 	assign out1 = flip(flip(inp)); | ||||||
|  | 	assign out2 = help(flip(inp)); | ||||||
|  | 	assign out3 = help_mul(inp); | ||||||
|  | 	assign out4 = pow_flip_a(flip(inp), 3); | ||||||
|  | 	assign out5 = pow_flip_b(2, 2); | ||||||
|  | endmodule | ||||||
							
								
								
									
										33
									
								
								tests/simple/const_func_shadow.v
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								tests/simple/const_func_shadow.v
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | ||||||
|  | module top(w, x, y, z); | ||||||
|  | 	function [11:0] func; | ||||||
|  | 		input reg [2:0] x; | ||||||
|  | 		input reg [2:0] y; | ||||||
|  | 		begin | ||||||
|  | 			x = x * (y + 1); | ||||||
|  | 			begin : foo | ||||||
|  | 				reg [2:0] y; | ||||||
|  | 				y = x + 1; | ||||||
|  | 				begin : bar | ||||||
|  | 					reg [2:0] x; | ||||||
|  | 					x = y + 1; | ||||||
|  | 					begin : blah | ||||||
|  | 						reg [2:0] y; | ||||||
|  | 						y = x + 1; | ||||||
|  | 						func[2:0] = y; | ||||||
|  | 					end | ||||||
|  | 					func[5:3] = x; | ||||||
|  | 				end | ||||||
|  | 				func[8:6] = y; | ||||||
|  | 			end | ||||||
|  | 			func[11:9] = x; | ||||||
|  | 		end | ||||||
|  | 	endfunction | ||||||
|  | 	output wire [func(2, 3) - 1:0] w; | ||||||
|  | 	output wire [func(1, 3) - 1:0] x; | ||||||
|  | 	output wire [func(3, 1) - 1:0] y; | ||||||
|  | 	output wire [func(5, 2) - 1:0] z; | ||||||
|  | 	assign w = 1'sb1; | ||||||
|  | 	assign x = 1'sb1; | ||||||
|  | 	assign y = 1'sb1; | ||||||
|  | 	assign z = 1'sb1; | ||||||
|  | endmodule | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue