Template:GNU C Compiler Internals/Instrumenting an AST 4 1
Lookup of a Declaration in the Symbol Table
editvoid gem_find_symtab(tree *t_var, char *name) { tree t_ident = get_identifier(name); if (t_ident) *t_var = lookup_name(t_ident); else *t_var=NULL_TREE; }
Building Tree Nodes
editThe walk_tree callback function can instrument the AST. Functions build1() and build() construct new tree nodes. The former function takes one operand, the latter one takes more than one operand. The following code computes the address of the operand, same as '&' C operator:
t = build1(ADDR_EXPR, TREE_TYPE(t), t);
The following example refers to an array element arr[0]:
t = build(ARRAY_REF, integer_type_node, arr, integer_zero_node);
The following example builds an integer constant:
t = build_int_cst(NULL_TREE, 123);
Building a string constant is more difficult. The following example demonstrates the idea:
tree gem_build_string_literal(int len, const char *str) { tree t, elem, index, type; t = build_string (len, str); elem = build_type_variant (char_type_node, 1, 0); index = build_index_type (build_int_2(len-1, 0)); type = build_array_type (elem, index); T_T(t) = type; TREE_READONLY(t)=1; TREE_STATIC(t)=1; TREE_CONSTANT(t)=1; type=build_pointer_type (type); t = build1 (ADDR_EXPR, type, t); t = build1 (NOP_EXPR, build_pointer_type(char_type_node), t); return t; }
To build a function call one needs to find the function's declaration and build the list of arguments. Then the CALL_EXPR is constructed:
gem_find_symtab(&t_func_decl, "func"); t_arg1 = build_tree_list(NULL_TREE, arg1); t_arg2 = build_tree_list(NULL_TREE, arg2); ... TREE_CHAIN(t_arg1)=t_arg2; ... TREE_CHAIN(t_argn)=NULL_TREE; t_call = build_function_call(t_func_decl, t_arg1);
If you want to build a list of statements { stmt1; stmt2; ... }, then you need to use function append_to_statement_list():
tree list=NULL_TREE; for (i=0; i<num_stmt; i++) { BUILD_FUNC_CALL1(t_call, t_send, t_arr[i], NULL_TREE); append_to_statement_list(t_call, &list); }
Adding Nodes to a Tree
editGCC 4.1 has an interface that allows one to add a chain of nodes into another chain of nodes implemented in file tree-iterator.c. Functions tsi_start() and tsi_last() create a tree statement iterator and assigns it to the first or the last tree in the list, respectively. Functions tsi_link_before() and tsi_link_after() link a statement using the iterator either before or after the current statement. There is also function append_to_statement_list() that adds a node to a list. If the specified list argument is NULL_TREE then a new statement list is allocated.
Building Function and Variable Declarations
editA global declaration is added in hook gem_c_common_nodes_and_builtins(). In this following example we build a structure type and create a global variable of this type. The structure has a field of type unsigned int and a function pointer field.
t_log = make_node(RECORD_TYPE); decl_chain = NULL_TREE; field_decl = build_decl(FIELD_DECL, get_identifier("addr"), unsigned_type_node); TREE_CHAIN(field_decl)=decl_chain; decl_chain=field_decl; DECL_FIELD_CONTEXT(decl_chain) = t_log; ... t_func_type = build_function_type_list(void_type_node, unsigned_type_node, NULL_TREE); field_decl = build_decl(FIELD_DECL, get_identifier("add_addr"), build_pointer_type(t_func_type); TREE_CHAIN(field_decl)=decl_chain; decl_chain=field_decl; DECL_FIELD_CONTEXT(decl_chain) = t_log; ... TYPE_FIELDS(t_log) = nreverse(decl_chain); layout_type(t_log); pushdecl(build_decl(TYPE_DECL, get_identifier("log_t"), t_log)); decl = build_decl(VAR_DECL, get_identifier("log"), build_pointer_type(t_log)); DECL_EXTERNAL(decl)=1; pushdecl(decl);