diff --git a/IR/.clang-format b/IR/.clang-format new file mode 100644 index 00000000..0bbb1991 --- /dev/null +++ b/IR/.clang-format @@ -0,0 +1,689 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# clang-format configuration file. Intended for clang-format >= 11. +# +# For more information, see: +# +# Documentation/process/clang-format.rst +# https://clang.llvm.org/docs/ClangFormat.html +# https://clang.llvm.org/docs/ClangFormatStyleOptions.html +# +--- +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: false +AlignConsecutiveDeclarations: false +AlignEscapedNewlines: Left +AlignOperands: true +AlignTrailingComments: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: false +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: None +AllowShortIfStatementsOnASingleLine: false +AllowShortLoopsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: None +AlwaysBreakAfterReturnType: None +AlwaysBreakBeforeMultilineStrings: false +AlwaysBreakTemplateDeclarations: false +BinPackArguments: true +BinPackParameters: true +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + AfterExternBlock: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: true + SplitEmptyRecord: true + SplitEmptyNamespace: true +BreakBeforeBinaryOperators: None +BreakBeforeBraces: Custom +BreakBeforeInheritanceComma: false +BreakBeforeTernaryOperators: false +BreakConstructorInitializersBeforeComma: false +BreakConstructorInitializers: BeforeComma +BreakAfterJavaFieldAnnotations: false +BreakStringLiterals: false +ColumnLimit: 80 +CommentPragmas: '^ IWYU pragma:' +CompactNamespaces: false +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ConstructorInitializerIndentWidth: 8 +ContinuationIndentWidth: 8 +Cpp11BracedListStyle: false +DerivePointerAlignment: false +DisableFormat: false +ExperimentalAutoDetectBinPacking: false +FixNamespaceComments: false + +# Taken from: +# git grep -h '^#define [^[:space:]]*for_each[^[:space:]]*(' include/ tools/ \ +# | sed "s,^#define \([^[:space:]]*for_each[^[:space:]]*\)(.*$, - '\1'," \ +# | LC_ALL=C sort -u +ForEachMacros: + - '__ata_qc_for_each' + - '__bio_for_each_bvec' + - '__bio_for_each_segment' + - '__evlist__for_each_entry' + - '__evlist__for_each_entry_continue' + - '__evlist__for_each_entry_from' + - '__evlist__for_each_entry_reverse' + - '__evlist__for_each_entry_safe' + - '__for_each_mem_range' + - '__for_each_mem_range_rev' + - '__for_each_thread' + - '__hlist_for_each_rcu' + - '__map__for_each_symbol_by_name' + - '__perf_evlist__for_each_entry' + - '__perf_evlist__for_each_entry_reverse' + - '__perf_evlist__for_each_entry_safe' + - '__rq_for_each_bio' + - '__shost_for_each_device' + - 'apei_estatus_for_each_section' + - 'ata_for_each_dev' + - 'ata_for_each_link' + - 'ata_qc_for_each' + - 'ata_qc_for_each_raw' + - 'ata_qc_for_each_with_internal' + - 'ax25_for_each' + - 'ax25_uid_for_each' + - 'bio_for_each_bvec' + - 'bio_for_each_bvec_all' + - 'bio_for_each_folio_all' + - 'bio_for_each_integrity_vec' + - 'bio_for_each_segment' + - 'bio_for_each_segment_all' + - 'bio_list_for_each' + - 'bip_for_each_vec' + - 'bond_for_each_slave' + - 'bond_for_each_slave_rcu' + - 'bpf__perf_for_each_map' + - 'bpf__perf_for_each_map_named' + - 'bpf_for_each_spilled_reg' + - 'bpf_object__for_each_map' + - 'bpf_object__for_each_program' + - 'bpf_object__for_each_safe' + - 'bpf_perf_object__for_each' + - 'btree_for_each_safe128' + - 'btree_for_each_safe32' + - 'btree_for_each_safe64' + - 'btree_for_each_safel' + - 'card_for_each_dev' + - 'cgroup_taskset_for_each' + - 'cgroup_taskset_for_each_leader' + - 'cpufreq_for_each_efficient_entry_idx' + - 'cpufreq_for_each_entry' + - 'cpufreq_for_each_entry_idx' + - 'cpufreq_for_each_valid_entry' + - 'cpufreq_for_each_valid_entry_idx' + - 'css_for_each_child' + - 'css_for_each_descendant_post' + - 'css_for_each_descendant_pre' + - 'damon_for_each_region' + - 'damon_for_each_region_safe' + - 'damon_for_each_scheme' + - 'damon_for_each_scheme_safe' + - 'damon_for_each_target' + - 'damon_for_each_target_safe' + - 'data__for_each_file' + - 'data__for_each_file_new' + - 'data__for_each_file_start' + - 'device_for_each_child_node' + - 'displayid_iter_for_each' + - 'dma_fence_array_for_each' + - 'dma_fence_chain_for_each' + - 'dma_fence_unwrap_for_each' + - 'dma_resv_for_each_fence' + - 'dma_resv_for_each_fence_unlocked' + - 'do_for_each_ftrace_op' + - 'drm_atomic_crtc_for_each_plane' + - 'drm_atomic_crtc_state_for_each_plane' + - 'drm_atomic_crtc_state_for_each_plane_state' + - 'drm_atomic_for_each_plane_damage' + - 'drm_client_for_each_connector_iter' + - 'drm_client_for_each_modeset' + - 'drm_connector_for_each_possible_encoder' + - 'drm_for_each_bridge_in_chain' + - 'drm_for_each_connector_iter' + - 'drm_for_each_crtc' + - 'drm_for_each_crtc_reverse' + - 'drm_for_each_encoder' + - 'drm_for_each_encoder_mask' + - 'drm_for_each_fb' + - 'drm_for_each_legacy_plane' + - 'drm_for_each_plane' + - 'drm_for_each_plane_mask' + - 'drm_for_each_privobj' + - 'drm_mm_for_each_hole' + - 'drm_mm_for_each_node' + - 'drm_mm_for_each_node_in_range' + - 'drm_mm_for_each_node_safe' + - 'dsa_switch_for_each_available_port' + - 'dsa_switch_for_each_cpu_port' + - 'dsa_switch_for_each_port' + - 'dsa_switch_for_each_port_continue_reverse' + - 'dsa_switch_for_each_port_safe' + - 'dsa_switch_for_each_user_port' + - 'dsa_tree_for_each_user_port' + - 'dso__for_each_symbol' + - 'dsos__for_each_with_build_id' + - 'elf_hash_for_each_possible' + - 'elf_section__for_each_rel' + - 'elf_section__for_each_rela' + - 'elf_symtab__for_each_symbol' + - 'evlist__for_each_cpu' + - 'evlist__for_each_entry' + - 'evlist__for_each_entry_continue' + - 'evlist__for_each_entry_from' + - 'evlist__for_each_entry_reverse' + - 'evlist__for_each_entry_safe' + - 'flow_action_for_each' + - 'for_each_acpi_dev_match' + - 'for_each_active_dev_scope' + - 'for_each_active_drhd_unit' + - 'for_each_active_iommu' + - 'for_each_active_route' + - 'for_each_aggr_pgid' + - 'for_each_available_child_of_node' + - 'for_each_bench' + - 'for_each_bio' + - 'for_each_board_func_rsrc' + - 'for_each_btf_ext_rec' + - 'for_each_btf_ext_sec' + - 'for_each_bvec' + - 'for_each_card_auxs' + - 'for_each_card_auxs_safe' + - 'for_each_card_components' + - 'for_each_card_dapms' + - 'for_each_card_pre_auxs' + - 'for_each_card_prelinks' + - 'for_each_card_rtds' + - 'for_each_card_rtds_safe' + - 'for_each_card_widgets' + - 'for_each_card_widgets_safe' + - 'for_each_cgroup_storage_type' + - 'for_each_child_of_node' + - 'for_each_clear_bit' + - 'for_each_clear_bit_from' + - 'for_each_clear_bitrange' + - 'for_each_clear_bitrange_from' + - 'for_each_cmd' + - 'for_each_cmsghdr' + - 'for_each_collection' + - 'for_each_comp_order' + - 'for_each_compatible_node' + - 'for_each_component_dais' + - 'for_each_component_dais_safe' + - 'for_each_console' + - 'for_each_console_srcu' + - 'for_each_cpu' + - 'for_each_cpu_and' + - 'for_each_cpu_wrap' + - 'for_each_dapm_widgets' + - 'for_each_dedup_cand' + - 'for_each_dev_addr' + - 'for_each_dev_scope' + - 'for_each_dma_cap_mask' + - 'for_each_dpcm_be' + - 'for_each_dpcm_be_rollback' + - 'for_each_dpcm_be_safe' + - 'for_each_dpcm_fe' + - 'for_each_drhd_unit' + - 'for_each_dss_dev' + - 'for_each_efi_memory_desc' + - 'for_each_efi_memory_desc_in_map' + - 'for_each_element' + - 'for_each_element_extid' + - 'for_each_element_id' + - 'for_each_endpoint_of_node' + - 'for_each_event' + - 'for_each_event_tps' + - 'for_each_evictable_lru' + - 'for_each_fib6_node_rt_rcu' + - 'for_each_fib6_walker_rt' + - 'for_each_free_mem_pfn_range_in_zone' + - 'for_each_free_mem_pfn_range_in_zone_from' + - 'for_each_free_mem_range' + - 'for_each_free_mem_range_reverse' + - 'for_each_func_rsrc' + - 'for_each_group_device' + - 'for_each_group_evsel' + - 'for_each_group_member' + - 'for_each_hstate' + - 'for_each_if' + - 'for_each_inject_fn' + - 'for_each_insn' + - 'for_each_insn_prefix' + - 'for_each_intid' + - 'for_each_iommu' + - 'for_each_ip_tunnel_rcu' + - 'for_each_irq_nr' + - 'for_each_lang' + - 'for_each_link_codecs' + - 'for_each_link_cpus' + - 'for_each_link_platforms' + - 'for_each_lru' + - 'for_each_matching_node' + - 'for_each_matching_node_and_match' + - 'for_each_mem_pfn_range' + - 'for_each_mem_range' + - 'for_each_mem_range_rev' + - 'for_each_mem_region' + - 'for_each_member' + - 'for_each_memory' + - 'for_each_migratetype_order' + - 'for_each_missing_reg' + - 'for_each_net' + - 'for_each_net_continue_reverse' + - 'for_each_net_rcu' + - 'for_each_netdev' + - 'for_each_netdev_continue' + - 'for_each_netdev_continue_rcu' + - 'for_each_netdev_continue_reverse' + - 'for_each_netdev_feature' + - 'for_each_netdev_in_bond_rcu' + - 'for_each_netdev_rcu' + - 'for_each_netdev_reverse' + - 'for_each_netdev_safe' + - 'for_each_new_connector_in_state' + - 'for_each_new_crtc_in_state' + - 'for_each_new_mst_mgr_in_state' + - 'for_each_new_plane_in_state' + - 'for_each_new_plane_in_state_reverse' + - 'for_each_new_private_obj_in_state' + - 'for_each_new_reg' + - 'for_each_node' + - 'for_each_node_by_name' + - 'for_each_node_by_type' + - 'for_each_node_mask' + - 'for_each_node_state' + - 'for_each_node_with_cpus' + - 'for_each_node_with_property' + - 'for_each_nonreserved_multicast_dest_pgid' + - 'for_each_of_allnodes' + - 'for_each_of_allnodes_from' + - 'for_each_of_cpu_node' + - 'for_each_of_pci_range' + - 'for_each_old_connector_in_state' + - 'for_each_old_crtc_in_state' + - 'for_each_old_mst_mgr_in_state' + - 'for_each_old_plane_in_state' + - 'for_each_old_private_obj_in_state' + - 'for_each_oldnew_connector_in_state' + - 'for_each_oldnew_crtc_in_state' + - 'for_each_oldnew_mst_mgr_in_state' + - 'for_each_oldnew_plane_in_state' + - 'for_each_oldnew_plane_in_state_reverse' + - 'for_each_oldnew_private_obj_in_state' + - 'for_each_online_cpu' + - 'for_each_online_node' + - 'for_each_online_pgdat' + - 'for_each_path' + - 'for_each_pci_bridge' + - 'for_each_pci_dev' + - 'for_each_pcm_streams' + - 'for_each_physmem_range' + - 'for_each_populated_zone' + - 'for_each_possible_cpu' + - 'for_each_present_cpu' + - 'for_each_prime_number' + - 'for_each_prime_number_from' + - 'for_each_probe_cache_entry' + - 'for_each_process' + - 'for_each_process_thread' + - 'for_each_prop_codec_conf' + - 'for_each_prop_dai_codec' + - 'for_each_prop_dai_cpu' + - 'for_each_prop_dlc_codecs' + - 'for_each_prop_dlc_cpus' + - 'for_each_prop_dlc_platforms' + - 'for_each_property_of_node' + - 'for_each_reg' + - 'for_each_reg_filtered' + - 'for_each_registered_fb' + - 'for_each_requested_gpio' + - 'for_each_requested_gpio_in_range' + - 'for_each_reserved_mem_range' + - 'for_each_reserved_mem_region' + - 'for_each_rtd_codec_dais' + - 'for_each_rtd_components' + - 'for_each_rtd_cpu_dais' + - 'for_each_rtd_dais' + - 'for_each_script' + - 'for_each_sec' + - 'for_each_set_bit' + - 'for_each_set_bit_from' + - 'for_each_set_bitrange' + - 'for_each_set_bitrange_from' + - 'for_each_set_clump8' + - 'for_each_sg' + - 'for_each_sg_dma_page' + - 'for_each_sg_page' + - 'for_each_sgtable_dma_page' + - 'for_each_sgtable_dma_sg' + - 'for_each_sgtable_page' + - 'for_each_sgtable_sg' + - 'for_each_shell_test' + - 'for_each_sibling_event' + - 'for_each_subelement' + - 'for_each_subelement_extid' + - 'for_each_subelement_id' + - 'for_each_sublist' + - 'for_each_subsystem' + - 'for_each_supported_activate_fn' + - 'for_each_supported_inject_fn' + - 'for_each_test' + - 'for_each_thread' + - 'for_each_token' + - 'for_each_unicast_dest_pgid' + - 'for_each_vsi' + - 'for_each_wakeup_source' + - 'for_each_zone' + - 'for_each_zone_zonelist' + - 'for_each_zone_zonelist_nodemask' + - 'func_for_each_insn' + - 'fwnode_for_each_available_child_node' + - 'fwnode_for_each_child_node' + - 'fwnode_graph_for_each_endpoint' + - 'gadget_for_each_ep' + - 'genradix_for_each' + - 'genradix_for_each_from' + - 'hash_for_each' + - 'hash_for_each_possible' + - 'hash_for_each_possible_rcu' + - 'hash_for_each_possible_rcu_notrace' + - 'hash_for_each_possible_safe' + - 'hash_for_each_rcu' + - 'hash_for_each_safe' + - 'hashmap__for_each_entry' + - 'hashmap__for_each_entry_safe' + - 'hashmap__for_each_key_entry' + - 'hashmap__for_each_key_entry_safe' + - 'hctx_for_each_ctx' + - 'hists__for_each_format' + - 'hists__for_each_sort_list' + - 'hlist_bl_for_each_entry' + - 'hlist_bl_for_each_entry_rcu' + - 'hlist_bl_for_each_entry_safe' + - 'hlist_for_each' + - 'hlist_for_each_entry' + - 'hlist_for_each_entry_continue' + - 'hlist_for_each_entry_continue_rcu' + - 'hlist_for_each_entry_continue_rcu_bh' + - 'hlist_for_each_entry_from' + - 'hlist_for_each_entry_from_rcu' + - 'hlist_for_each_entry_rcu' + - 'hlist_for_each_entry_rcu_bh' + - 'hlist_for_each_entry_rcu_notrace' + - 'hlist_for_each_entry_safe' + - 'hlist_for_each_entry_srcu' + - 'hlist_for_each_safe' + - 'hlist_nulls_for_each_entry' + - 'hlist_nulls_for_each_entry_from' + - 'hlist_nulls_for_each_entry_rcu' + - 'hlist_nulls_for_each_entry_safe' + - 'i3c_bus_for_each_i2cdev' + - 'i3c_bus_for_each_i3cdev' + - 'idr_for_each_entry' + - 'idr_for_each_entry_continue' + - 'idr_for_each_entry_continue_ul' + - 'idr_for_each_entry_ul' + - 'in_dev_for_each_ifa_rcu' + - 'in_dev_for_each_ifa_rtnl' + - 'inet_bind_bucket_for_each' + - 'inet_lhash2_for_each_icsk' + - 'inet_lhash2_for_each_icsk_continue' + - 'inet_lhash2_for_each_icsk_rcu' + - 'interval_tree_for_each_double_span' + - 'interval_tree_for_each_span' + - 'intlist__for_each_entry' + - 'intlist__for_each_entry_safe' + - 'iopt_for_each_contig_area' + - 'kcore_copy__for_each_phdr' + - 'key_for_each' + - 'key_for_each_safe' + - 'klp_for_each_func' + - 'klp_for_each_func_safe' + - 'klp_for_each_func_static' + - 'klp_for_each_object' + - 'klp_for_each_object_safe' + - 'klp_for_each_object_static' + - 'kunit_suite_for_each_test_case' + - 'kvm_for_each_memslot' + - 'kvm_for_each_memslot_in_gfn_range' + - 'kvm_for_each_vcpu' + - 'libbpf_nla_for_each_attr' + - 'list_for_each' + - 'list_for_each_codec' + - 'list_for_each_codec_safe' + - 'list_for_each_continue' + - 'list_for_each_entry' + - 'list_for_each_entry_continue' + - 'list_for_each_entry_continue_rcu' + - 'list_for_each_entry_continue_reverse' + - 'list_for_each_entry_from' + - 'list_for_each_entry_from_rcu' + - 'list_for_each_entry_from_reverse' + - 'list_for_each_entry_lockless' + - 'list_for_each_entry_rcu' + - 'list_for_each_entry_reverse' + - 'list_for_each_entry_safe' + - 'list_for_each_entry_safe_continue' + - 'list_for_each_entry_safe_from' + - 'list_for_each_entry_safe_reverse' + - 'list_for_each_entry_srcu' + - 'list_for_each_from' + - 'list_for_each_prev' + - 'list_for_each_prev_safe' + - 'list_for_each_safe' + - 'llist_for_each' + - 'llist_for_each_entry' + - 'llist_for_each_entry_safe' + - 'llist_for_each_safe' + - 'map__for_each_symbol' + - 'map__for_each_symbol_by_name' + - 'map_for_each_event' + - 'map_for_each_metric' + - 'maps__for_each_entry' + - 'maps__for_each_entry_safe' + - 'mci_for_each_dimm' + - 'media_device_for_each_entity' + - 'media_device_for_each_intf' + - 'media_device_for_each_link' + - 'media_device_for_each_pad' + - 'msi_for_each_desc' + - 'nanddev_io_for_each_page' + - 'netdev_for_each_lower_dev' + - 'netdev_for_each_lower_private' + - 'netdev_for_each_lower_private_rcu' + - 'netdev_for_each_mc_addr' + - 'netdev_for_each_uc_addr' + - 'netdev_for_each_upper_dev_rcu' + - 'netdev_hw_addr_list_for_each' + - 'nft_rule_for_each_expr' + - 'nla_for_each_attr' + - 'nla_for_each_nested' + - 'nlmsg_for_each_attr' + - 'nlmsg_for_each_msg' + - 'nr_neigh_for_each' + - 'nr_neigh_for_each_safe' + - 'nr_node_for_each' + - 'nr_node_for_each_safe' + - 'of_for_each_phandle' + - 'of_property_for_each_string' + - 'of_property_for_each_u32' + - 'pci_bus_for_each_resource' + - 'pci_dev_for_each_resource' + - 'pcl_for_each_chunk' + - 'pcl_for_each_segment' + - 'pcm_for_each_format' + - 'perf_config_items__for_each_entry' + - 'perf_config_sections__for_each_entry' + - 'perf_config_set__for_each_entry' + - 'perf_cpu_map__for_each_cpu' + - 'perf_evlist__for_each_entry' + - 'perf_evlist__for_each_entry_reverse' + - 'perf_evlist__for_each_entry_safe' + - 'perf_evlist__for_each_evsel' + - 'perf_evlist__for_each_mmap' + - 'perf_hpp_list__for_each_format' + - 'perf_hpp_list__for_each_format_safe' + - 'perf_hpp_list__for_each_sort_list' + - 'perf_hpp_list__for_each_sort_list_safe' + - 'perf_pmu__for_each_hybrid_pmu' + - 'ping_portaddr_for_each_entry' + - 'ping_portaddr_for_each_entry_rcu' + - 'plist_for_each' + - 'plist_for_each_continue' + - 'plist_for_each_entry' + - 'plist_for_each_entry_continue' + - 'plist_for_each_entry_safe' + - 'plist_for_each_safe' + - 'pnp_for_each_card' + - 'pnp_for_each_dev' + - 'protocol_for_each_card' + - 'protocol_for_each_dev' + - 'queue_for_each_hw_ctx' + - 'radix_tree_for_each_slot' + - 'radix_tree_for_each_tagged' + - 'rb_for_each' + - 'rbtree_postorder_for_each_entry_safe' + - 'rdma_for_each_block' + - 'rdma_for_each_port' + - 'rdma_umem_for_each_dma_block' + - 'resort_rb__for_each_entry' + - 'resource_list_for_each_entry' + - 'resource_list_for_each_entry_safe' + - 'rhl_for_each_entry_rcu' + - 'rhl_for_each_rcu' + - 'rht_for_each' + - 'rht_for_each_entry' + - 'rht_for_each_entry_from' + - 'rht_for_each_entry_rcu' + - 'rht_for_each_entry_rcu_from' + - 'rht_for_each_entry_safe' + - 'rht_for_each_from' + - 'rht_for_each_rcu' + - 'rht_for_each_rcu_from' + - 'rq_for_each_bvec' + - 'rq_for_each_segment' + - 'rq_list_for_each' + - 'rq_list_for_each_safe' + - 'scsi_for_each_prot_sg' + - 'scsi_for_each_sg' + - 'sctp_for_each_hentry' + - 'sctp_skb_for_each' + - 'sec_for_each_insn' + - 'sec_for_each_insn_continue' + - 'sec_for_each_insn_from' + - 'shdma_for_each_chan' + - 'shost_for_each_device' + - 'sk_for_each' + - 'sk_for_each_bound' + - 'sk_for_each_entry_offset_rcu' + - 'sk_for_each_from' + - 'sk_for_each_rcu' + - 'sk_for_each_safe' + - 'sk_nulls_for_each' + - 'sk_nulls_for_each_from' + - 'sk_nulls_for_each_rcu' + - 'snd_array_for_each' + - 'snd_pcm_group_for_each_entry' + - 'snd_soc_dapm_widget_for_each_path' + - 'snd_soc_dapm_widget_for_each_path_safe' + - 'snd_soc_dapm_widget_for_each_sink_path' + - 'snd_soc_dapm_widget_for_each_source_path' + - 'strlist__for_each_entry' + - 'strlist__for_each_entry_safe' + - 'sym_for_each_insn' + - 'sym_for_each_insn_continue_reverse' + - 'symbols__for_each_entry' + - 'tb_property_for_each' + - 'tcf_act_for_each_action' + - 'tcf_exts_for_each_action' + - 'udp_portaddr_for_each_entry' + - 'udp_portaddr_for_each_entry_rcu' + - 'usb_hub_for_each_child' + - 'v4l2_device_for_each_subdev' + - 'v4l2_m2m_for_each_dst_buf' + - 'v4l2_m2m_for_each_dst_buf_safe' + - 'v4l2_m2m_for_each_src_buf' + - 'v4l2_m2m_for_each_src_buf_safe' + - 'virtio_device_for_each_vq' + - 'while_for_each_ftrace_op' + - 'xa_for_each' + - 'xa_for_each_marked' + - 'xa_for_each_range' + - 'xa_for_each_start' + - 'xas_for_each' + - 'xas_for_each_conflict' + - 'xas_for_each_marked' + - 'xbc_array_for_each_value' + - 'xbc_for_each_key_value' + - 'xbc_node_for_each_array_value' + - 'xbc_node_for_each_child' + - 'xbc_node_for_each_key_value' + - 'xbc_node_for_each_subkey' + - 'zorro_for_each_dev' + +IncludeBlocks: Preserve +IncludeCategories: + - Regex: '.*' + Priority: 1 +IncludeIsMainRegex: '(Test)?$' +IndentCaseLabels: false +IndentGotoLabels: false +IndentPPDirectives: None +IndentWidth: 8 +IndentWrappedFunctionNames: false +JavaScriptQuotes: Leave +JavaScriptWrapImports: true +KeepEmptyLinesAtTheStartOfBlocks: false +MacroBlockBegin: '' +MacroBlockEnd: '' +MaxEmptyLinesToKeep: 1 +NamespaceIndentation: None +ObjCBinPackProtocolList: Auto +ObjCBlockIndentWidth: 8 +ObjCSpaceAfterProperty: true +ObjCSpaceBeforeProtocolList: true + +# Taken from git's rules +PenaltyBreakAssignment: 10 +PenaltyBreakBeforeFirstCallParameter: 30 +PenaltyBreakComment: 10 +PenaltyBreakFirstLessLess: 0 +PenaltyBreakString: 10 +PenaltyExcessCharacter: 100 +PenaltyReturnTypeOnItsOwnLine: 60 + +PointerAlignment: Right +ReflowComments: false +SortIncludes: false +SortUsingDeclarations: false +SpaceAfterCStyleCast: false +SpaceAfterTemplateKeyword: true +SpaceBeforeAssignmentOperators: true +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatementsExceptForEachMacros +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 1 +SpacesInAngles: false +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +Standard: Cpp03 +TabWidth: 8 +UseTab: Always +... diff --git a/IR/.clang-tidy b/IR/.clang-tidy new file mode 100644 index 00000000..4ca5b0f0 --- /dev/null +++ b/IR/.clang-tidy @@ -0,0 +1,8 @@ +--- +Checks: 'clang-diagnostic-*,-*,misc-*,performance-*,clang-analyzer-*,readability-function-size,readability-identifier-naming,readability-redundant-*,readability-mis*,readability-string-compare,readability-non-const-parameter,-clang-analyzer-security*,cppcoreguidelines-avoid-non-const-global-variables,-misc-no-recursion,bugprone-assignment-in-if-condition,bugprone-infinite-loop,bugprone-integer-division,bugprone-suspicious-string-compare,llvm-header-guard,google-global-names-in-headers,cppcoreguidelines-no-malloc,bugprone-use-after-move' +WarningsAsErrors: '*,-misc-non-private-member-variables-in-classes' +CheckOptions: + - key: readability-function-size.LineThreshold + value: '300' +... + diff --git a/IR/CMakeLists.txt b/IR/CMakeLists.txt index 04b3eb5b..9d3293db 100644 --- a/IR/CMakeLists.txt +++ b/IR/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.20) project(bpf_ir) -set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wextra -O3") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror -Wstrict-prototypes -O3") add_executable( read @@ -8,27 +8,14 @@ add_executable( bpf_ir.c array.c ir_helper.c + ir_value.c ir_bb.c ir_insn.c passes/phi_pass.c - passes/reachable_bb.c passes/add_counter_pass.c passes/add_constraint_pass.c passes/cut_bb_pass.c - passes/end_bb.c - aux/live_variable.c aux/prog_check.c - aux/eliminate_ssa.c - aux/conflict_analysis.c - aux/graph_coloring.c - aux/explicit_reg.c - aux/coaleasing.c - aux/spill.c - aux/translate.c - aux/stack_alloc.c - aux/normalize.c - aux/fix_bb_succ.c - aux/relocate.c ir_code_gen.c ) diff --git a/IR/Makefile b/IR/Makefile new file mode 100644 index 00000000..dc3bfeda --- /dev/null +++ b/IR/Makefile @@ -0,0 +1,13 @@ +build: + cmake --build build + +run: build + build/read tests/${NAME}.o + +kernel: build + ./scripts/gen_kernel.sh + +format: + ./scripts/format.sh + +.PHONY: build run format diff --git a/IR/Readme.md b/IR/Readme.md index a12aad09..f856ef0a 100644 --- a/IR/Readme.md +++ b/IR/Readme.md @@ -12,6 +12,8 @@ One opinion, one benefit of designing the raw constraint from is that our runtim - [x] Register spilling - [x] Caller-saved/callee-saved register +- [ ] Fix: stack offset should be changed before CG +- [ ] VR type & Constant type (inference) - [ ] Translation # TODO diff --git a/IR/array.c b/IR/array.c index 5158c71d..2e0fa0e6 100644 --- a/IR/array.c +++ b/IR/array.c @@ -1,95 +1,105 @@ -#include "array.h" -#include -#include +#include -void *__malloc(size_t size) { - void *data = malloc(size); - memset(data, 0, size); - return data; +void bpf_ir_array_init(struct array *res, size_t size) +{ + res->data = NULL; + res->max_elem = 0; + res->elem_size = size; + res->num_elem = 0; } -void __free(void *ptr) { - free(ptr); +struct array bpf_ir_array_null(void) +{ + struct array res; + res.data = NULL; + res.max_elem = 0; + res.elem_size = 0; + res.num_elem = 0; + return res; } -struct array array_init(size_t size) { - struct array res; - res.data = __malloc(size * 4); - res.max_elem = 4; - res.elem_size = size; - res.num_elem = 0; - return res; +int bpf_ir_array_push(struct array *arr, void *data) +{ + if (arr->data == NULL) { + SAFE_MALLOC(arr->data, arr->elem_size * 2) + arr->max_elem = 2; + } + if (arr->num_elem >= arr->max_elem) { + // Reallocate + void *new_data = NULL; + SAFE_MALLOC(new_data, arr->max_elem * 2 * arr->elem_size); + memcpy(new_data, arr->data, arr->num_elem * arr->elem_size); + free_proto(arr->data); + arr->data = new_data; + arr->max_elem *= 2; + } + // Push back + memcpy((char *)(arr->data) + arr->elem_size * arr->num_elem, data, + arr->elem_size); + arr->num_elem++; + return 0; } -struct array array_null() { - struct array res; - res.data = NULL; - res.max_elem = 0; - res.elem_size = 0; - res.num_elem = 0; - return res; +int bpf_ir_array_push_unique(struct array *arr, void *data) +{ + for (size_t i = 0; i < arr->num_elem; ++i) { + if (memcmp((char *)(arr->data) + arr->elem_size * i, data, + arr->elem_size) == 0) { + return 0; + } + } + return bpf_ir_array_push(arr, data); } -void array_push(struct array *arr, void *data) { - if (arr->num_elem >= arr->max_elem) { - // Reallocate - void *new_data = __malloc(arr->max_elem * 2 * arr->elem_size); - memcpy(new_data, arr->data, arr->num_elem * arr->elem_size); - __free(arr->data); - arr->data = new_data; - arr->max_elem *= 2; - } - // Push back - memcpy((char *)(arr->data) + arr->elem_size * arr->num_elem, data, arr->elem_size); - arr->num_elem++; +void bpf_ir_array_erase(struct array *arr, size_t idx) +{ + if (idx >= arr->num_elem) { + return; + } + // Shift elements + for (size_t i = idx; i < arr->num_elem - 1; ++i) { + memcpy((char *)(arr->data) + arr->elem_size * i, + (char *)(arr->data) + arr->elem_size * (i + 1), + arr->elem_size); + } + arr->num_elem--; } -void array_push_unique(struct array *arr, void *data) { - for (size_t i = 0; i < arr->num_elem; ++i) { - if (memcmp((char *)(arr->data) + arr->elem_size * i, data, arr->elem_size) == 0) { - return; - } - } - array_push(arr, data); +int bpf_ir_array_clear(struct array *arr) +{ + free_proto(arr->data); + SAFE_MALLOC(arr->data, arr->elem_size * 4); + arr->max_elem = 4; + arr->num_elem = 0; + return 0; } -void array_erase(struct array *arr, size_t idx) { - if (idx >= arr->num_elem) { - return; - } - // Shift elements - for (size_t i = idx; i < arr->num_elem - 1; ++i) { - memcpy((char *)(arr->data) + arr->elem_size * i, - (char *)(arr->data) + arr->elem_size * (i + 1), arr->elem_size); - } - arr->num_elem--; +int bpf_ir_array_clone(struct array *res, struct array *arr) +{ + res->num_elem = arr->num_elem; + res->max_elem = arr->max_elem; + res->elem_size = arr->elem_size; + if (arr->num_elem == 0) { + res->data = NULL; + return 0; + } + SAFE_MALLOC(res->data, arr->max_elem * arr->elem_size); + memcpy(res->data, arr->data, arr->num_elem * arr->elem_size); + return 0; } -void array_clear(struct array *arr) { - __free(arr->data); - arr->data = __malloc(arr->elem_size * 4); - arr->max_elem = 4; - arr->num_elem = 0; +void bpf_ir_array_free(struct array *arr) +{ + if (arr->data) { + free_proto(arr->data); + } + *arr = bpf_ir_array_null(); } -struct array array_clone(struct array *arr) { - struct array res; - res.num_elem = arr->num_elem; - res.max_elem = arr->max_elem; - res.elem_size = arr->elem_size; - res.data = __malloc(arr->max_elem * arr->elem_size); - memcpy(res.data, arr->data, arr->num_elem * arr->elem_size); - return res; -} - -void array_free(struct array *arr) { - __free(arr->data); - *arr = array_null(); -} - -void *array_get_void(struct array *arr, size_t idx) { - if (idx >= arr->num_elem) { - return NULL; - } - return (char *)(arr->data) + arr->elem_size * idx; +void *bpf_ir_array_get_void(struct array *arr, size_t idx) +{ + if (idx >= arr->num_elem) { + return NULL; + } + return (char *)(arr->data) + arr->elem_size * idx; } diff --git a/IR/aux/coaleasing.c b/IR/aux/coaleasing.c deleted file mode 100644 index 672533f3..00000000 --- a/IR/aux/coaleasing.c +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include "bpf_ir.h" -#include "code_gen.h" -#include "dbg.h" -#include "ir_fun.h" -#include "ir_insn.h" -#include "list.h" - -void coaleasing(struct ir_function *fun) { - struct ir_basic_block **pos; - // For each BB - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_insn *pos2, *tmp; - // For each operation - list_for_each_entry_safe(pos2, tmp, &bb->ir_insn_head, list_ptr) { - struct ir_insn *insn_dst = dst(pos2); - if (pos2->op == IR_INSN_ASSIGN) { - if (pos2->values[0].type == IR_VALUE_INSN) { - struct ir_insn *src = pos2->values[0].data.insn_d; - DBGASSERT(src == dst(src)); - if (insn_cg(src)->alloc_reg == insn_cg(insn_dst)->alloc_reg) { - // Remove - erase_insn_raw(pos2); - } - } - } - } - } -} diff --git a/IR/aux/conflict_analysis.c b/IR/aux/conflict_analysis.c deleted file mode 100644 index 4f3e748c..00000000 --- a/IR/aux/conflict_analysis.c +++ /dev/null @@ -1,118 +0,0 @@ -#include -#include -#include "array.h" -#include "bpf_ir.h" -#include "code_gen.h" -#include "dbg.h" -#include "ir_helper.h" -#include "list.h" - -int is_final(struct ir_insn *v1) { - return v1 == dst(v1); -} - -void build_conflict(struct ir_insn *v1, struct ir_insn *v2) { - if (!is_final(v1) || !is_final(v2)) { - CRITICAL("Can only build conflict on final values"); - } - if (v1 == v2) { - return; - } - array_push_unique(&insn_cg(v1)->adj, &v2); - array_push_unique(&insn_cg(v2)->adj, &v1); -} - -void print_interference_graph(struct ir_function *fun) { - // Tag the IR to have the actual number to print - tag_ir(fun); - struct ir_insn **pos; - array_for(pos, fun->cg_info.all_var) { - struct ir_insn *insn = *pos; - if (insn->op == IR_INSN_REG) { - CRITICAL("Pre-colored register should not be in all_var"); - } - if (!is_final(insn)) { - // Not final value, give up - CRITICAL("Not Final Value!"); - } - struct ir_insn_cg_extra *extra = insn_cg(insn); - if (extra->allocated) { - // Allocated VR - printf("%%%zu(", insn->_insn_id); - if (extra->spilled) { - printf("sp-%zu", extra->spilled * 8); - } else { - printf("r%u", extra->alloc_reg); - } - printf("):"); - } else { - // Pre-colored registers or unallocated VR - print_insn_ptr_base(insn); - printf(":"); - } - struct ir_insn **pos2; - array_for(pos2, insn_cg(insn)->adj) { - struct ir_insn *adj_insn = *pos2; - if (!is_final(adj_insn)) { - // Not final value, give up - CRITICAL("Not Final Value!"); - } - printf(" "); - print_insn_ptr_base(adj_insn); - } - printf("\n"); - } -} - -void caller_constraint(struct ir_function *fun, struct ir_insn *insn) { - for (__u8 i = BPF_REG_0; i < BPF_REG_6; ++i) { - // R0-R5 are caller saved register - DBGASSERT(fun->cg_info.regs[i] == dst(fun->cg_info.regs[i])); - build_conflict(fun->cg_info.regs[i], insn); - } -} - -void conflict_analysis(struct ir_function *fun) { - // Basic conflict: - // For every x in KILL set, x is conflict with every element in OUT set. - - struct ir_basic_block **pos; - // For each BB - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_insn *insn; - // For each operation - list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - struct ir_insn_cg_extra *insn_cg = insn->user_data; - if (insn->op == IR_INSN_CALL) { - // Add caller saved register constraints - struct ir_insn **pos2; - array_for(pos2, insn_cg->in) { - DBGASSERT(*pos2 == dst(*pos2)); - struct ir_insn **pos3; - array_for(pos3, insn_cg->out) { - DBGASSERT(*pos3 == dst(*pos3)); - if (*pos2 == *pos3) { - // Live across CALL! - printf("Found a VR live across CALL!\n"); - caller_constraint(fun, *pos2); - } - } - } - } - struct ir_insn **pos2; - array_for(pos2, insn_cg->kill) { - struct ir_insn *insn_dst = *pos2; - DBGASSERT(insn_dst == dst(insn_dst)); - if (insn_dst->op != IR_INSN_REG) { - array_push_unique(&fun->cg_info.all_var, &insn_dst); - } - struct ir_insn **pos3; - array_for(pos3, insn_cg->out) { - DBGASSERT(*pos3 == dst(*pos3)); - build_conflict(insn_dst, *pos3); - } - } - } - } -} diff --git a/IR/aux/eliminate_ssa.c b/IR/aux/eliminate_ssa.c deleted file mode 100644 index 9bc1bccd..00000000 --- a/IR/aux/eliminate_ssa.c +++ /dev/null @@ -1,92 +0,0 @@ -#include "code_gen.h" -#include "array.h" -#include "bpf_ir.h" -#include "dbg.h" -#include "ir_fun.h" -#include "ir_insn.h" - -// Convert from TSSA to CSSA -// Using "Method I" in paper "Translating Out of Static Single Assignment Form" -void to_cssa(struct ir_function *fun) { - struct array phi_insns = INIT_ARRAY(struct ir_insn *); - - struct ir_basic_block **pos; - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_insn *insn; - list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - if (insn->op == IR_INSN_PHI) { - array_push(&phi_insns, &insn); - } else { - break; - } - } - } - - struct ir_insn **pos2; - array_for(pos2, phi_insns) { - struct ir_insn *insn = *pos2; - // Create the moved PHI insn - struct ir_insn *new_phi = create_phi_insn(insn, INSERT_FRONT); - struct phi_value *pos3; - array_for(pos3, insn->phi) { - struct ir_insn *new_insn = - create_assign_insn_bb(pos3->bb, pos3->value, INSERT_BACK_BEFORE_JMP); - // Remove use - val_remove_user(pos3->value, insn); - phi_add_operand(new_phi, pos3->bb, ir_value_insn(new_insn)); - } - - array_free(&insn->phi); - insn->op = IR_INSN_ASSIGN; - struct ir_value val = ir_value_insn(new_phi); - insn->values[0] = val; - insn->value_num = 1; - val_add_user(val, insn); - } - - array_free(&phi_insns); -} - -// Remove PHI insn -void remove_phi(struct ir_function *fun) { - // dst information ready - struct array phi_insns = INIT_ARRAY(struct ir_insn *); - - struct ir_basic_block **pos; - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_insn *insn; - list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - if (insn->op == IR_INSN_PHI) { - array_push(&phi_insns, &insn); - } else { - break; - } - } - } - - struct ir_insn **pos2; - array_for(pos2, phi_insns) { - struct ir_insn *insn = *pos2; - struct ir_insn *repr = NULL; - struct phi_value *pos3; - array_for(pos3, insn->phi) { - if (!repr) { - repr = pos3->value.data.insn_d; - } else { - insn_cg(pos3->value.data.insn_d)->dst = repr; - } - } - if (!repr) { - CRITICAL("Empty Phi not removed!"); - } - - DBGASSERT(repr == dst(repr)); - - replace_all_usage(insn, ir_value_insn(repr)); - erase_insn(insn); - } - - array_free(&phi_insns); -} diff --git a/IR/aux/explicit_reg.c b/IR/aux/explicit_reg.c deleted file mode 100644 index b7ce7567..00000000 --- a/IR/aux/explicit_reg.c +++ /dev/null @@ -1,69 +0,0 @@ -// Make register usage explicit -// Example: -// %x = add %y, %arg1 -// arg1 is r0 at the beginning of the function -// We then add a new instruction to the beginning of the function. - -#include -#include "array.h" -#include "bpf_ir.h" -#include "code_gen.h" -#include "dbg.h" -#include "ir_insn.h" -#include "ir_helper.h" - -void explicit_reg(struct ir_function *fun) { - // fun is still in IR form - // Before this step, users are correct - // In this step we change some dsts - // We need carefully handle the users - // dsts are NOT users - // Invariant: All operands are final values - // Final value: v == dst(v) - struct ir_basic_block **pos; - // Maximum number of functions: MAX_FUNC_ARG - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_insn *insn; - list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - if (insn->op == IR_INSN_CALL) { - for (__u8 i = 0; i < insn->value_num; ++i) { - struct ir_value val = insn->values[i]; - struct ir_insn *new_insn = create_assign_insn_cg(insn, val, INSERT_FRONT); - insn_cg(new_insn)->dst = fun->cg_info.regs[i + 1]; - val_remove_user(val, insn); - } - insn->value_num = 0; // Remove all operands - struct ir_insn_cg_extra *extra = insn_cg(insn); - extra->dst = NULL; - if (insn->users.num_elem == 0) { - continue; - } - struct ir_insn *new_insn = - create_assign_insn_cg(insn, ir_value_insn(fun->cg_info.regs[0]), INSERT_BACK); - replace_all_usage(insn, ir_value_insn(new_insn)); - } - - if (insn->op == IR_INSN_RET) { - // ret x - // ==> - // R0 = x - // ret - struct ir_insn *new_insn = - create_assign_insn_cg(insn, insn->values[0], INSERT_FRONT); - val_remove_user(insn->values[0], insn); - insn_cg(new_insn)->dst = fun->cg_info.regs[0]; - insn->value_num = 0; - } - } - } - // Arg - for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { - if (fun->function_arg[i]->users.num_elem > 0) { - // Insert ASSIGN arg[i] at the beginning of the function - struct ir_insn *new_insn = create_assign_insn_bb_cg( - fun->entry, ir_value_insn(fun->cg_info.regs[i + 1]), INSERT_FRONT_AFTER_PHI); - replace_all_usage(fun->function_arg[i], ir_value_insn(new_insn)); - } - } -} diff --git a/IR/aux/fix_bb_succ.c b/IR/aux/fix_bb_succ.c deleted file mode 100644 index 561b7c7f..00000000 --- a/IR/aux/fix_bb_succ.c +++ /dev/null @@ -1,19 +0,0 @@ -#include "bpf_ir.h" -#include "dbg.h" -#include "ir_bb.h" -#include "ir_fun.h" - -void fix_bb_succ(struct ir_function *fun) { - struct ir_basic_block **pos; - array_for(pos, fun->all_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_insn *insn = get_last_insn(bb); - if (insn && insn->op >= IR_INSN_JEQ && insn->op < IR_INSN_PHI) { - // Conditional jmp - struct ir_basic_block **s1 = array_get(&bb->succs, 0, struct ir_basic_block *); - struct ir_basic_block **s2 = array_get(&bb->succs, 1, struct ir_basic_block *); - *s1 = insn->bb1; - *s2 = insn->bb2; - } - } -} diff --git a/IR/aux/graph_coloring.c b/IR/aux/graph_coloring.c deleted file mode 100644 index 2406ae40..00000000 --- a/IR/aux/graph_coloring.c +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include -#include -#include "array.h" -#include "bpf_ir.h" -#include "code_gen.h" -#include "dbg.h" -#include "ir_helper.h" - -int compare_insn(const void *a, const void *b) { - struct ir_insn *ap = *(struct ir_insn **)a; - struct ir_insn *bp = *(struct ir_insn **)b; - return ap->_insn_id > bp->_insn_id; -} - -void graph_coloring(struct ir_function *fun) { - // Using the Chaitin's Algorithm - // Using the simple dominance heuristic (Simple traversal of BB) - tag_ir(fun); - struct array *all_var = &fun->cg_info.all_var; - qsort(all_var->data, all_var->num_elem, all_var->elem_size, &compare_insn); - // all_var is now PEO - struct ir_insn **pos; - array_for(pos, (*all_var)) { - // Allocate register for *pos - struct ir_insn *insn = *pos; - if (insn->op == IR_INSN_REG) { - CRITICAL("Pre-colored register should not be in all_var"); - } - struct ir_insn_cg_extra *extra = insn_cg(insn); - struct ir_insn **pos2; - - int used_reg[MAX_BPF_REG] = {0}; - struct array used_spill = INIT_ARRAY(size_t); - array_for(pos2, extra->adj) { - struct ir_insn *insn2 = *pos2; // Adj instruction - struct ir_insn_cg_extra *extra2 = insn_cg(insn2); - if (extra2->allocated) { - if (extra2->spilled) { - array_push_unique(&used_spill, &extra2->spilled); - } else { - used_reg[extra2->alloc_reg] = 1; - } - } - } - __u8 need_spill = 1; - for (__u8 i = 0; i < MAX_BPF_REG; i++) { - if (!used_reg[i]) { - extra->allocated = 1; - printf("Allocate r%u for %%%zu\n", i, insn->_insn_id); - extra->alloc_reg = i; - need_spill = 0; - break; - } - } - if (need_spill) { - size_t sp = 1; - while (1) { - __u8 found = 1; - size_t *pos3; - array_for(pos3, used_spill) { - if (*pos3 == sp) { - sp++; - found = 0; - break; - } - } - if (found) { - extra->allocated = 1; - extra->spilled = sp; - break; - } - } - } - array_free(&used_spill); - } -} diff --git a/IR/aux/live_variable.c b/IR/aux/live_variable.c deleted file mode 100644 index b9956a61..00000000 --- a/IR/aux/live_variable.c +++ /dev/null @@ -1,187 +0,0 @@ -// Live variable analysis -#include -#include -#include "array.h" -#include "bpf_ir.h" -#include "code_gen.h" -#include "dbg.h" -#include "ir_bb.h" -#include "ir_fun.h" -#include "ir_insn.h" -#include "list.h" -#include "ir_helper.h" - -void array_erase_elem(struct array *arr, struct ir_insn *insn) { - // Remove insn from arr - for (size_t i = 0; i < arr->num_elem; ++i) { - struct ir_insn *pos = ((struct ir_insn **)(arr->data))[i]; - if (pos == insn) { - array_erase(arr, i); - return; - } - } -} - -void gen_kill(struct ir_function *fun) { - struct ir_basic_block **pos; - // For each BB - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_insn *pos2; - // For each operation - list_for_each_entry(pos2, &bb->ir_insn_head, list_ptr) { - struct ir_insn *insn_dst = dst(pos2); - struct ir_insn_cg_extra *insn_cg = pos2->user_data; - if (!is_void(pos2) && insn_dst) { - array_push_unique(&insn_cg->kill, &insn_dst); - } - struct array value_uses = get_operands(pos2); - struct ir_value **pos3; - array_for(pos3, value_uses) { - struct ir_value *val = *pos3; - if (val->type == IR_VALUE_INSN) { - struct ir_insn *insn = val->data.insn_d; - DBGASSERT(insn == dst(insn)); - array_push_unique(&insn_cg->gen, &insn); - // array_erase_elem(&insn_cg->kill, insn); - } - } - array_free(&value_uses); - } - } -} - -int array_contains(struct array *arr, struct ir_insn *insn) { - struct ir_insn **pos; - array_for(pos, (*arr)) { - if (*pos == insn) { - return 1; - } - } - return 0; -} - -struct array array_delta(struct array *a, struct array *b) { - struct array res = INIT_ARRAY(struct ir_insn *); - struct ir_insn **pos; - array_for(pos, (*a)) { - struct ir_insn *insn = *pos; - if (!array_contains(b, insn)) { - array_push(&res, &insn); - } - } - return res; -} - -void merge_array(struct array *a, struct array *b) { - struct ir_insn **pos; - array_for(pos, (*b)) { - struct ir_insn *insn = *pos; - array_push_unique(a, &insn); - } -} - -int equal_set(struct array *a, struct array *b) { - if (a->num_elem != b->num_elem) { - return 0; - } - struct ir_insn **pos; - array_for(pos, (*a)) { - struct ir_insn *insn = *pos; - if (!array_contains(b, insn)) { - return 0; - } - } - return 1; -} - -void in_out(struct ir_function *fun) { - int change = 1; - // For each BB - while (change) { - change = 0; - struct ir_basic_block **pos; - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_insn *insn; - - list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - struct ir_insn_cg_extra *insn_cg = insn->user_data; - struct array old_in = insn_cg->in; - array_clear(&insn_cg->out); - - if (get_last_insn(bb) == insn) { - // Last instruction - struct ir_basic_block **pos2; - array_for(pos2, bb->succs) { - struct ir_basic_block *bb2 = *pos2; - if (bb_empty(bb2)) { - CRITICAL("Found empty BB"); - } - struct ir_insn *first = get_first_insn(bb2); - struct ir_insn_cg_extra *insn2_cg = first->user_data; - merge_array(&insn_cg->out, &insn2_cg->in); - } - } else { - // Not last instruction - struct ir_insn *next_insn = - list_entry(insn->list_ptr.next, struct ir_insn, list_ptr); - struct ir_insn_cg_extra *next_insn_cg = next_insn->user_data; - merge_array(&insn_cg->out, &next_insn_cg->in); - } - struct array out_kill_delta = array_delta(&insn_cg->out, &insn_cg->kill); - insn_cg->in = array_clone(&insn_cg->gen); - merge_array(&insn_cg->in, &out_kill_delta); - // Check for change - if (!equal_set(&insn_cg->in, &old_in)) { - change = 1; - } - // Collect grabage - array_free(&out_kill_delta); - array_free(&old_in); - } - } - } -} - -void print_insn_extra(struct ir_insn *insn) { - struct ir_insn_cg_extra *insn_cg = insn->user_data; - if (insn_cg == NULL) { - CRITICAL("NULL user data"); - } - printf("--\nGen:"); - struct ir_insn **pos; - array_for(pos, insn_cg->gen) { - struct ir_insn *insn = *pos; - printf(" "); - print_insn_ptr_base(insn); - } - printf("\nKill:"); - array_for(pos, insn_cg->kill) { - struct ir_insn *insn = *pos; - printf(" "); - print_insn_ptr_base(insn); - } - printf("\nIn:"); - array_for(pos, insn_cg->in) { - struct ir_insn *insn = *pos; - printf(" "); - print_insn_ptr_base(insn); - } - printf("\nOut:"); - array_for(pos, insn_cg->out) { - struct ir_insn *insn = *pos; - printf(" "); - print_insn_ptr_base(insn); - } - printf("\n-------------\n"); -} - -void liveness_analysis(struct ir_function *fun) { - // TODO: Encode Calling convention into GEN KILL - gen_kill(fun); - in_out(fun); - printf("--------------\n"); - print_ir_prog_advanced(fun, NULL, print_insn_extra, print_ir_dst); - print_ir_prog_advanced(fun, NULL, NULL, print_ir_dst); -} diff --git a/IR/aux/normalize.c b/IR/aux/normalize.c deleted file mode 100644 index cfec5bd1..00000000 --- a/IR/aux/normalize.c +++ /dev/null @@ -1,116 +0,0 @@ -// Normalization - -#include "bpf_ir.h" -#include "code_gen.h" -#include "dbg.h" -#include "ir_fun.h" -#include "ir_insn.h" - -void normalize(struct ir_function *fun) { - struct ir_basic_block **pos; - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_insn *insn; - list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - struct ir_value *v0 = &insn->values[0]; - struct ir_value *v1 = &insn->values[1]; - enum val_type t0 = insn->value_num >= 1 ? vtype(*v0) : UNDEF; - enum val_type t1 = insn->value_num >= 2 ? vtype(*v1) : UNDEF; - enum val_type tdst = vtype_insn(insn); - struct ir_insn *dst_insn = dst(insn); - if (insn->op == IR_INSN_ALLOC) { - // Skip - } else if (insn->op == IR_INSN_STORE) { - // Should be converted to ASSIGN - CRITICAL("Error"); - } else if (insn->op == IR_INSN_LOAD) { - CRITICAL("Error"); - } else if (insn->op == IR_INSN_LOADRAW) { - // OK - } else if (insn->op == IR_INSN_STORERAW) { - // OK - } else if (insn->op >= IR_INSN_ADD && insn->op < IR_INSN_CALL) { - // Binary ALU - if (t0 == STACK && t1 == CONST) { - // reg1 = add stack const - // ==> - // reg1 = stack - // reg1 = add reg1 const - struct ir_insn *new_insn = create_assign_insn_cg(insn, *v0, INSERT_FRONT); - insn_cg(new_insn)->dst = dst_insn; - v0->type = IR_VALUE_INSN; - v0->data.insn_d = dst_insn; - } else if (t0 == REG && t1 == REG) { - // reg1 = add reg2 reg3 - __u8 reg1 = insn_cg(dst_insn)->alloc_reg; - __u8 reg2 = insn_cg(v0->data.insn_d)->alloc_reg; - __u8 reg3 = insn_cg(v1->data.insn_d)->alloc_reg; - if (reg1 != reg2) { - if (reg1 == reg3) { - // Exchange reg2 and reg3 - struct ir_value tmp = *v0; - *v0 = *v1; - *v1 = tmp; - } else { - // reg1 = add reg2 reg3 - // ==> - // reg1 = reg2 - // reg1 = add reg1 reg3 - struct ir_insn *new_insn = - create_assign_insn_cg(insn, *v0, INSERT_FRONT); - DBGASSERT(dst_insn == fun->cg_info.regs[reg1]); - insn_cg(new_insn)->dst = dst_insn; - v0->type = IR_VALUE_INSN; - v0->data.insn_d = dst_insn; - } - } - } else if (t0 == REG && t1 == CONST) { - // reg1 = add reg2 const - // ==> - // reg1 = reg2 - // reg1 = add reg1 const - struct ir_insn *new_insn = create_assign_insn_cg(insn, *v0, INSERT_FRONT); - insn_cg(new_insn)->dst = dst_insn; - v0->type = IR_VALUE_INSN; - v0->data.insn_d = dst_insn; - } else { - CRITICAL("Error"); - } - } else if (insn->op == IR_INSN_ASSIGN) { - // stack = reg - // stack = const - // reg = const - // reg = stack - // reg = reg - if (tdst == STACK) { - DBGASSERT(t0 != STACK); - // Change to STORERAW - insn->op = IR_INSN_STORERAW; - insn->addr_val.value = ir_value_stack_ptr(); - insn->addr_val.offset = -insn_cg(dst_insn)->spilled * 8; - insn->vr_type = IR_VR_TYPE_64; - } else { - if (t0 == STACK) { - // Change to LOADRAW - insn->op = IR_INSN_LOADRAW; - insn->addr_val.value = ir_value_stack_ptr(); - insn->addr_val.offset = -insn_cg(v0->data.insn_d)->spilled * 8; - insn->vr_type = IR_VR_TYPE_64; - } - } - } else if (insn->op == IR_INSN_RET) { - // OK - } else if (insn->op == IR_INSN_CALL) { - // OK - } else if (insn->op == IR_INSN_JA) { - // OK - } else if (insn->op >= IR_INSN_JEQ && insn->op < IR_INSN_PHI) { - // jeq reg const/reg - DBGASSERT(t0 == REG && (t1 == REG || t1 == CONST)); - // OK - } else { - CRITICAL("No such instruction"); - } - } - } -} diff --git a/IR/aux/prog_check.c b/IR/aux/prog_check.c index 676fdc30..a1ef97de 100644 --- a/IR/aux/prog_check.c +++ b/IR/aux/prog_check.c @@ -1,109 +1,336 @@ -#include "prog_check.h" -#include "array.h" -#include "bpf_ir.h" -#include "dbg.h" -#include "ir_insn.h" -#include "list.h" - -void check_insn_users_use_insn(struct ir_insn *insn) { - struct ir_insn **pos; - array_for(pos, insn->users) { - struct ir_insn *user = *pos; - // Check if the user actually uses this instruction - struct array operands = get_operands(user); - struct ir_value **val; - int found = 0; - array_for(val, operands) { - struct ir_value *v = *val; - if (v->type == IR_VALUE_INSN && v->data.insn_d == insn) { - // Found the user - found = 1; - break; - } - } - array_free(&operands); - if (!found) { - // Error! - CRITICAL("User does not use the instruction"); - } - } +#include + +static void check_insn_users_use_insn(struct ir_insn *insn) +{ + struct ir_insn **pos; + array_for(pos, insn->users) + { + struct ir_insn *user = *pos; + // Check if the user actually uses this instruction + struct array operands = get_operands(user); + struct ir_value **val; + int found = 0; + array_for(val, operands) + { + struct ir_value *v = *val; + if (v->type == IR_VALUE_INSN && + v->data.insn_d == insn) { + // Found the user + found = 1; + break; + } + } + bpf_ir_array_free(&operands); + if (!found) { + // Error! + print_ir_insn_err(insn, "The instruction"); + print_ir_insn_err(user, "The user of that instruction"); + CRITICAL("User does not use the instruction"); + } + } } -void check_insn(struct ir_function *fun) { - // Check syntax - // - Store uses alloc - // - `ret` at the end of BB chains +static void check_insn(struct ir_function *fun) +{ + // Check syntax + // Check value num + // - Store uses alloc + // - Load uses alloc + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + if (insn->op == IR_INSN_LOADRAW || + insn->op == IR_INSN_ALLOC || + insn->op == IR_INSN_JA || insn->op == IR_INSN_PHI) { + if (!(insn->value_num == 0)) { + print_ir_insn_err(insn, NULL); + CRITICAL( + "Instruction should have no value"); + } + } + if (insn->op == IR_INSN_STORERAW || + insn->op == IR_INSN_LOAD || + insn->op == IR_INSN_RET) { + if (!(insn->value_num == 1)) { + print_ir_insn_err(insn, NULL); + CRITICAL( + "Instruction should have 1 values"); + } + } + + if (insn->op == IR_INSN_STORE || (is_alu(insn)) || + (is_cond_jmp(insn))) { + if (!(insn->value_num == 2)) { + print_ir_insn_err(insn, NULL); + CRITICAL( + "Instruction should have 2 values"); + } + } + + if (insn->op == IR_INSN_STORE || + insn->op == IR_INSN_LOAD) { + if (!(insn->values[0].type == IR_VALUE_INSN && + insn->values[0].data.insn_d->op == + IR_INSN_ALLOC)) { + print_ir_insn_err(insn, NULL); + CRITICAL( + "Value[0] should be an alloc instruction"); + } + } + + // TODO: Check: users of alloc instructions must be STORE/LOAD + + if (is_alu(insn) || is_cond_jmp(insn)) { + // Binary ALU + if (!bpf_ir_valid_alu_type(insn->alu)) { + print_ir_insn_err(insn, NULL); + CRITICAL("Binary ALU type error!"); + } + } + + if (insn->op == IR_INSN_ALLOC || + insn->op == IR_INSN_LOADRAW || + insn->op == IR_INSN_STORERAW) { + if (!bpf_ir_valid_vr_type(insn->vr_type)) { + print_ir_insn_err(insn, NULL); + CRITICAL("Invalid VR type"); + } + } + } + } } -void check_insn_operand(struct ir_insn *insn) { - struct array operands = get_operands(insn); - struct ir_value **val; - array_for(val, operands) { - struct ir_value *v = *val; - if (v->type == IR_VALUE_INSN) { - // Check if the operand actually is used by this instruction - struct ir_insn **pos2; - int found = 0; - array_for(pos2, v->data.insn_d->users) { - struct ir_insn *user = *pos2; - if (user == insn) { - // Found the user - found = 1; - break; - } - } - if (!found) { - // Error! - CRITICAL("Operand is not used by the instruction"); - } - } - } - array_free(&operands); +static void check_insn_operand(struct ir_insn *insn) +{ + struct array operands = get_operands(insn); + struct ir_value **val; + array_for(val, operands) + { + struct ir_value *v = *val; + if (v->type == IR_VALUE_INSN) { + // Check if the operand actually is used by this instruction + struct ir_insn **pos2; + int found = 0; + array_for(pos2, v->data.insn_d->users) + { + struct ir_insn *user = *pos2; + if (user == insn) { + // Found the user + found = 1; + break; + } + } + if (!found) { + // Error! + print_ir_insn_err(v->data.insn_d, + "Operand defined here"); + print_ir_insn_err( + insn, + "Instruction that uses the operand"); + CRITICAL( + "Instruction not found in the operand's users"); + } + } + } + bpf_ir_array_free(&operands); } // Check if the users are correct (only applicable to SSA IR form) -void check_users(struct ir_function *fun) { - // Check FunctionCallArgument Instructions - for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { - struct ir_insn *insn = fun->function_arg[i]; - check_insn_users_use_insn(insn); - } - struct ir_basic_block **pos; - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_insn *insn; - list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - // Check users of this instruction - check_insn_users_use_insn(insn); - // Check operands of this instruction - check_insn_operand(insn); - } - } +static void check_users(struct ir_function *fun) +{ + // Check FunctionCallArgument Instructions + for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { + struct ir_insn *insn = fun->function_arg[i]; + check_insn_users_use_insn(insn); + } + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + // Check users of this instruction + check_insn_users_use_insn(insn); + // Check operands of this instruction + check_insn_operand(insn); + } + } } -void check_jumping(struct ir_function *fun) {} +static void check_jumping(struct ir_function *fun) +{ + // Check if the jump instruction is at the end of the BB + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + + // check if BB is a succ of its preds + struct ir_basic_block **pred; + array_for(pred, bb->preds) + { + struct ir_basic_block *pred_bb = *pred; + struct ir_basic_block **succ; + int found = 0; + array_for(succ, pred_bb->succs) + { + struct ir_basic_block *succ_bb = *succ; + if (succ_bb == bb) { + found = 1; + break; + } + } + if (!found) { + // Error + print_ir_bb_err(bb); + PRINT_LOG("Pred: %zu\n", pred_bb->_id); + CRITICAL("BB not a succ of its pred"); + } + } + + struct ir_basic_block **succ; + array_for(succ, bb->succs) + { + struct ir_basic_block *succ_bb = *succ; + struct ir_basic_block **p; + int found = 0; + array_for(p, succ_bb->preds) + { + struct ir_basic_block *sp = *p; + if (sp == bb) { + found = 1; + break; + } + } + if (!found) { + // Error + print_ir_bb_err(bb); + CRITICAL("BB not a pred of its succ"); + } + } + + struct ir_insn *insn; + int jmp_exists = 0; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + if (is_jmp(insn)) { + jmp_exists = 1; + if (!is_last_insn(insn)) { + // Error + + print_ir_insn_err(insn, NULL); + CRITICAL( + "Jump statement not at the end of a BB"); + } else { + if (insn->op == IR_INSN_RET) { + if (bb->succs.num_elem != 0) { + // Error + + print_ir_insn_err(insn, + NULL); + CRITICAL( + "successor exists even after return statement"); + } + continue; + } + // For conditional jumps, both BB1 and BB2 should be successors + if (is_cond_jmp(insn)) { + // Get the two basic blocks that the conditional jump statement jumps to + struct ir_basic_block *bb1 = + insn->bb1; + struct ir_basic_block *bb2 = + insn->bb2; + // Check if the two basic blocks are successors of the current BB + if (bb->succs.num_elem != 2) { + CRITICAL( + "BB succs error"); + } + if (*array_get( + &bb->succs, 0, + struct ir_basic_block + *) != bb1 || + *array_get( + &bb->succs, 1, + struct ir_basic_block + *) != bb2) { + // Error + CRITICAL( + "Conditional jump statement with operands that are not successors " + "of the current BB"); + } + } else { + // For unconditional jumps, there should be only one successor + if (bb->succs.num_elem != 1) { + // Error + + print_ir_insn_err(insn, + NULL); + CRITICAL( + "Unconditional jump statement with more than one successor"); + } + // Check if the jump operand is the only successor of BB + if (*array_get( + &bb->succs, 0, + struct ir_basic_block + *) != + insn->bb1) { + // Error + + print_ir_insn_err(insn, + NULL); + CRITICAL( + "The jump operand is not the only successor of BB"); + } + } + } + } + } + // If there is no jump instruction (means no ret), there should be one successor + if (!jmp_exists) { + if (bb->succs.num_elem != 1) { + // Error + print_ir_bb_err(bb); + CRITICAL("Succ num error"); + } + } + } +} // Check if the PHI nodes are at the beginning of the BB -void check_phi(struct ir_function *fun) { - struct ir_basic_block **pos; - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - int all_phi = 1; - struct ir_insn *insn; - list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - if (insn->op == IR_INSN_PHI) { - if (!all_phi) { - // Error! - CRITICAL("Phi node not at the beginning of a BB"); - } - } else { - all_phi = 0; - } - } - } +static void check_phi(struct ir_function *fun) +{ + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + int all_phi = 1; + struct ir_insn *insn; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + if (insn->op == IR_INSN_PHI) { + if (!all_phi) { + // Error! + print_ir_insn_err(insn, NULL); + CRITICAL( + "Phi node not at the beginning of a BB"); + } + } else { + all_phi = 0; + } + } + } } +// Interface Implementation + // Check that the program is valid and able to be compiled -void prog_check(struct ir_function *fun) { - check_phi(fun); +void bpf_ir_prog_check(struct ir_function *fun) +{ + print_ir_err_init(fun); + + check_insn(fun); + check_phi(fun); + check_users(fun); + check_jumping(fun); } diff --git a/IR/aux/relocate.c b/IR/aux/relocate.c deleted file mode 100644 index b74e55f7..00000000 --- a/IR/aux/relocate.c +++ /dev/null @@ -1,8 +0,0 @@ -// Relocate BB -#include "ir_fun.h" - -void calc_bb_insn_cnt(struct ir_function *fun) {} - -void relocate(struct ir_function *fun) { - calc_bb_insn_cnt(fun); -} diff --git a/IR/aux/spill.c b/IR/aux/spill.c deleted file mode 100644 index 423b6fbf..00000000 --- a/IR/aux/spill.c +++ /dev/null @@ -1,339 +0,0 @@ -#include -#include -#include -#include "array.h" -#include "bpf_ir.h" -#include "code_gen.h" -#include "dbg.h" -#include "ir_insn.h" -#include "ir_helper.h" - -enum val_type vtype_insn(struct ir_insn *insn) { - insn = dst(insn); - if (insn == NULL) { - // Void - return UNDEF; - } - struct ir_insn_cg_extra *extra = insn_cg(insn); - if (extra->spilled) { - return STACK; - } else { - return REG; - } -} - -enum val_type vtype(struct ir_value val) { - if (val.type == IR_VALUE_INSN) { - return vtype_insn(val.data.insn_d); - } else if (val.type == IR_VALUE_CONSTANT) { - return CONST; - } else if (val.type == IR_VALUE_STACK_PTR) { - return REG; - } else { - CRITICAL("No such value type for dst"); - } -} - -void load_stack_to_r0(struct ir_function *fun, struct ir_insn *insn, struct ir_value *val) { - struct ir_insn *tmp = create_assign_insn_cg(insn, *val, INSERT_FRONT); - insn_cg(tmp)->dst = fun->cg_info.regs[0]; - - val->type = IR_VALUE_INSN; - val->data.insn_d = fun->cg_info.regs[0]; -} - -void load_const_to_vr(struct ir_insn *insn, struct ir_value *val) { - struct ir_insn *tmp = create_assign_insn_cg(insn, *val, INSERT_FRONT); - - val->type = IR_VALUE_INSN; - val->data.insn_d = tmp; -} - -void add_stack_offset_vr(struct ir_function *fun, size_t num) { - struct ir_insn **pos; - array_for(pos, fun->cg_info.all_var) { - struct ir_insn_cg_extra *extra = insn_cg(*pos); - if (extra->spilled > 0) { - extra->spilled += num; - } - } -} - -void spill_callee(struct ir_function *fun) { - // Spill Callee saved registers if used - __u8 reg_used[MAX_BPF_REG] = {0}; - - struct ir_insn **pos; - array_for(pos, fun->cg_info.all_var) { - struct ir_insn_cg_extra *extra = insn_cg(*pos); - reg_used[extra->alloc_reg] = 1; - } - size_t off = 0; - for (__u8 i = BPF_REG_6; i < BPF_REG_10; ++i) { - if (reg_used[i]) { - off++; - } - } - DBGASSERT(off == fun->cg_info.callee_num); - add_stack_offset_vr(fun, off); - off = 0; - for (__u8 i = BPF_REG_6; i < BPF_REG_10; ++i) { - // All callee saved registers - if (reg_used[i]) { - off++; - // Spill at sp-off - // struct ir_insn *st = create_assign_insn_bb_cg( - // fun->entry, ir_value_insn(fun->cg_info.regs[i]), INSERT_FRONT); - struct ir_insn *st = create_insn_base_cg(fun->entry); - insert_at_bb(st, fun->entry, INSERT_FRONT); - st->op = IR_INSN_STORERAW; - st->values[0] = ir_value_insn(fun->cg_info.regs[i]); - st->value_num = 1; - st->vr_type = IR_VR_TYPE_64; - struct ir_value val; - val.type = IR_VALUE_STACK_PTR; - st->addr_val.value = val; - st->addr_val.offset = -off * 8; - struct ir_insn_cg_extra *extra = insn_cg(st); - extra->dst = NULL; - - struct ir_basic_block **pos2; - array_for(pos2, fun->end_bbs) { - struct ir_basic_block *bb = *pos2; - struct ir_insn *ld = create_insn_base_cg(bb); - insert_at_bb(ld, bb, INSERT_BACK_BEFORE_JMP); - ld->op = IR_INSN_LOADRAW; - ld->value_num = 0; - ld->vr_type = IR_VR_TYPE_64; - struct ir_value val; - val.type = IR_VALUE_STACK_PTR; - ld->addr_val.value = val; - ld->addr_val.offset = -off * 8; - - extra = insn_cg(ld); - extra->dst = fun->cg_info.regs[i]; - } - } - } -} - -int check_need_spill(struct ir_function *fun) { - // Check if all instruction values are OK for translating - int res = 0; - struct ir_basic_block **pos; - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_insn *insn; - list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - struct ir_value *v0 = &insn->values[0]; - struct ir_value *v1 = &insn->values[1]; - enum val_type t0 = insn->value_num >= 1 ? vtype(*v0) : UNDEF; - enum val_type t1 = insn->value_num >= 2 ? vtype(*v1) : UNDEF; - enum val_type tdst = vtype_insn(insn); - struct ir_insn_cg_extra *extra = insn_cg(insn); - struct ir_insn *dst_insn = dst(insn); - if (insn->op == IR_INSN_ALLOC) { - // dst = alloc - // Nothing to do - } else if (insn->op == IR_INSN_STORE) { - // store v0(dst) v1 - // Eequivalent to `v0 = v1` - // TODO: sized store - // Currently all load & store are 8 bytes - insn->op = IR_INSN_ASSIGN; - DBGASSERT(v0->type == IR_VALUE_INSN); // Should be guaranteed by prog_check - extra->dst = v0->data.insn_d; - insn->value_num = 1; - *v0 = *v1; - res = 1; - } else if (insn->op == IR_INSN_LOAD) { - // stack = load stack - // stack = load reg - // reg = load reg - // reg = load stack - // TODO: sized load - insn->op = IR_INSN_ASSIGN; - DBGASSERT(v0->type == IR_VALUE_INSN); // Should be guaranteed by prog_check - res = 1; - } else if (insn->op == IR_INSN_LOADRAW) { - // reg = loadraw addr ==> OK - // stack = loadraw addr - // ==> - // R0 = loadraw addr - // stack = R0 - if (tdst == STACK) { - extra->dst = fun->cg_info.regs[0]; - struct ir_insn *tmp = create_assign_insn_cg( - insn, ir_value_insn(fun->cg_info.regs[0]), INSERT_BACK); - insn_cg(tmp)->dst = dst_insn; - res = 1; - } - } else if (insn->op == IR_INSN_STORERAW) { - // Built-in store instruction, OK - } else if (insn->op >= IR_INSN_ADD && insn->op < IR_INSN_CALL) { - // Binary ALU - // reg = add reg reg - // reg = add reg const - // There should be NO stack - if (tdst == STACK) { - // stack = add ? ? - // ==> - // R0 = add ? ? - // stack = R0 - extra->dst = fun->cg_info.regs[0]; - struct ir_insn *tmp = create_assign_insn_cg( - insn, ir_value_insn(fun->cg_info.regs[0]), INSERT_BACK); - insn_cg(tmp)->dst = dst_insn; - res = 1; - } else { - if ((t0 != REG && t1 == REG) || (t0 == CONST && t1 == STACK)) { - // reg = add !reg reg - // ==> - // reg = add reg !reg - struct ir_value tmp = *v0; - *v0 = *v1; - *v1 = tmp; - enum val_type ttmp = t0; - t0 = t1; - t1 = ttmp; - // No need to spill here - } - if (t0 == REG) { - // reg = add reg reg ==> OK - // reg = add reg const ==> OK - - // reg1 = add reg2 stack - // ==> - // If reg1 != reg2, - // reg1 = stack - // reg1 = add reg2 reg1 - // Else - // Choose reg3 != reg1, - // reg3 = stack - // reg1 = add reg1 reg3 - if (t1 == STACK) { - __u8 reg1 = insn_cg(dst_insn)->alloc_reg; - __u8 reg2 = insn_cg(v0->data.insn_d)->alloc_reg; - if (reg1 == reg2) { - __u8 reg = reg1 == 0 ? 1 : 0; - struct ir_insn *new_insn = - create_assign_insn_cg(insn, *v1, INSERT_FRONT); - insn_cg(new_insn)->dst = fun->cg_info.regs[reg]; - v1->type = IR_VALUE_INSN; - v1->data.insn_d = fun->cg_info.regs[reg]; - } else { - struct ir_insn *new_insn = - create_assign_insn_cg(insn, *v1, INSERT_FRONT); - insn_cg(new_insn)->dst = fun->cg_info.regs[reg1]; - v1->type = IR_VALUE_INSN; - v1->data.insn_d = fun->cg_info.regs[reg1]; - } - res = 1; - } - } else { - // reg = add const const ==> OK - // reg = add c1 c2 - // ==> - // reg = c1 - // reg = add reg c2 - // OK - - // reg = add stack stack - if (t0 == STACK && t1 == STACK) { - // reg1 = add stack1 stack2 - // ==> - // Found reg2 != reg1 - // reg1 = stack1 - // reg1 = add reg1 stack2 - __u8 reg1 = insn_cg(dst_insn)->alloc_reg; - struct ir_insn *new_insn = - create_assign_insn_cg(insn, *v0, INSERT_FRONT); - insn_cg(new_insn)->dst = fun->cg_info.regs[reg1]; - v0->type = IR_VALUE_INSN; - v0->data.insn_d = fun->cg_info.regs[reg1]; - res = 1; - } - // reg = add stack const ==> OK - // ==> - // reg = stack - // reg = add reg const - } - } - } else if (insn->op == IR_INSN_ASSIGN) { - // stack = reg - // stack = const - // reg = const - // reg = stack - // reg = reg - if (tdst == STACK && t0 == STACK) { - load_stack_to_r0(fun, insn, v0); - res = 1; - } - // TODO: constant to stack: might need to first load to reg - } else if (insn->op == IR_INSN_RET) { - // ret const/reg - // Done in explicit_reg pass - DBGASSERT(insn->value_num == 0); - } else if (insn->op == IR_INSN_CALL) { - // call() - // Should have no arguments - DBGASSERT(insn->value_num == 0); - } else if (insn->op == IR_INSN_JA) { - // OK - } else if (insn->op >= IR_INSN_JEQ && insn->op < IR_INSN_PHI) { - // jeq reg const/reg - if ((t0 != REG && t1 == REG) || (t0 == CONST && t1 == STACK)) { - struct ir_value tmp = *v0; - *v0 = *v1; - *v1 = tmp; - enum val_type ttmp = t0; - t0 = t1; - t1 = ttmp; - // No need to spill here - } - - if (t0 == REG) { - // jeq reg reg ==> OK - // jeq reg const ==> OK - // jeq reg stack - // ==> - // reg2 = stack - // jeq reg reg2 - if (t1 == STACK) { - __u8 reg1 = insn_cg(v0->data.insn_d)->alloc_reg; - __u8 reg2 = reg1 == 0 ? 1 : 0; - struct ir_insn *new_insn = create_assign_insn_cg(insn, *v1, INSERT_FRONT); - insn_cg(new_insn)->dst = fun->cg_info.regs[reg2]; - v1->type = IR_VALUE_INSN; - v1->data.insn_d = fun->cg_info.regs[reg2]; - res = 1; - } - } else { - // jeq const1 const2 - // ==> - // %tmp = const1 - // jeq %tmp const2 - if (t0 == CONST && t1 == CONST) { - struct ir_insn *new_insn = create_assign_insn_cg(insn, *v0, INSERT_FRONT); - v0->type = IR_VALUE_INSN; - v0->data.insn_d = new_insn; - res = 1; - } - // jeq stack const - if (t0 == STACK && t1 == CONST) { - load_stack_to_r0(fun, insn, v0); - res = 1; - } - // jeq stack stack - if (t0 == STACK && t1 == STACK) { - load_stack_to_r0(fun, insn, v0); - res = 1; - } - } - } else { - CRITICAL("No such instruction"); - } - } - } - return res; -} diff --git a/IR/aux/stack_alloc.c b/IR/aux/stack_alloc.c deleted file mode 100644 index 42498fb9..00000000 --- a/IR/aux/stack_alloc.c +++ /dev/null @@ -1,77 +0,0 @@ -// Allocate stack in code generation - -#include -#include "code_gen.h" -#include "ir_fun.h" -#include "array.h" -#include "bpf_ir.h" -#include "ir_insn.h" - -void calc_callee_num(struct ir_function *fun) { - __u8 reg_used[MAX_BPF_REG] = {0}; - - struct ir_insn **pos; - array_for(pos, fun->cg_info.all_var) { - struct ir_insn_cg_extra *extra = insn_cg(*pos); - reg_used[extra->alloc_reg] = 1; - } - size_t off = 0; - for (__u8 i = BPF_REG_6; i < BPF_REG_10; ++i) { - if (reg_used[i]) { - off++; - } - } - fun->cg_info.callee_num = off; -} - -void calc_stack_size(struct ir_function *fun) { - // Check callee - size_t off = 0; - off += fun->cg_info.callee_num * 8; - // Check all VR - size_t max = 0; - struct ir_insn **pos; - array_for(pos, fun->cg_info.all_var) { - struct ir_insn_cg_extra *extra = insn_cg(*pos); - if (extra->spilled > 0) { - // Spilled! - if (extra->spilled > max) { - max = extra->spilled; - } - } - } - fun->cg_info.stack_offset = -(off + max * 8); - printf("Stack size: %d\n", fun->cg_info.stack_offset); -} - -void add_stack_offset(struct ir_function *fun, __s16 offset) { - struct array users = fun->sp_users; - struct ir_insn **pos; - array_for(pos, users) { - struct ir_insn *insn = *pos; - - if (insn->op == IR_INSN_LOADRAW || insn->op == IR_INSN_STORERAW) { - insn->addr_val.offset += offset; - continue; - } - struct array value_uses = get_operands(insn); - struct ir_value **pos2; - array_for(pos2, value_uses) { - struct ir_value *val = *pos2; - if (val->type == IR_VALUE_STACK_PTR) { - // Stack pointer as value - struct ir_value new_val; - new_val.type = IR_VALUE_CONSTANT; - new_val.data.constant_d.type = IR_CONSTANT_S16; - new_val.data.constant_d.data.s16_d = offset; - struct ir_insn *new_insn = - create_bin_insn(insn, *val, new_val, IR_INSN_ADD, INSERT_FRONT); - new_val.type = IR_VALUE_INSN; - new_val.data.insn_d = new_insn; - *val = new_val; - } - } - array_free(&value_uses); - } - array_free(&fun->sp_users); -} diff --git a/IR/aux/translate.c b/IR/aux/translate.c deleted file mode 100644 index 52fa0b28..00000000 --- a/IR/aux/translate.c +++ /dev/null @@ -1,114 +0,0 @@ -#include -#include -#include "bpf_ir.h" -#include "code_gen.h" -#include "dbg.h" - -struct pre_ir_insn load_reg_to_reg(__u8 dst, __u8 src) { - // MOV dst src - struct pre_ir_insn insn; - insn.opcode = BPF_MOV | BPF_X | BPF_ALU64; - insn.dst_reg = dst; - insn.src_reg = src; - return insn; -} - -struct pre_ir_insn load_const_to_reg(__u8 dst, struct ir_constant c) { - // MOV dst imm - struct pre_ir_insn insn; - insn.dst_reg = dst; - if (c.type == IR_CONSTANT_U64) { - insn.it = IMM64; - insn.imm64 = c.data.u64_d; - insn.opcode = BPF_MOV | BPF_K | BPF_ALU64; - } - if (c.type == IR_CONSTANT_S64) { - insn.it = IMM64; - insn.imm64 = c.data.s64_d; - insn.opcode = BPF_MOV | BPF_K | BPF_ALU64; - } - if (c.type == IR_CONSTANT_U32) { - insn.it = IMM; - insn.imm = c.data.u32_d; - insn.opcode = BPF_MOV | BPF_K | BPF_ALU; - } - if (c.type == IR_CONSTANT_S32) { - insn.it = IMM; - insn.imm = c.data.s32_d; - insn.opcode = BPF_MOV | BPF_K | BPF_ALU; - } - if (c.type == IR_CONSTANT_U16) { - insn.it = IMM; - insn.imm = c.data.u16_d; - insn.opcode = BPF_MOV | BPF_K | BPF_ALU; - } - if (c.type == IR_CONSTANT_S16) { - insn.it = IMM; - insn.imm = c.data.s16_d; - insn.opcode = BPF_MOV | BPF_K | BPF_ALU; - } - return insn; -} - -struct pre_ir_insn load_addr_to_reg(__u8 dst, struct ir_address_value addr, enum ir_vr_type type) { - // MOV dst src - struct pre_ir_insn insn; - insn.dst_reg = dst; - insn.off = addr.offset; - int size = vr_type_to_size(type); - if (addr.value.type == IR_VALUE_STACK_PTR) { - insn.src_reg = BPF_REG_10; - insn.opcode = BPF_LDX | size | BPF_MEM; - } else if (addr.value.type == IR_VALUE_INSN) { - // Must be REG - DBGASSERT(vtype(addr.value) == REG); - // Load reg (addr) to reg - insn.src_reg = insn_cg(addr.value.data.insn_d)->alloc_reg; - insn.opcode = BPF_LDX | size | BPF_MEM; - } else if (addr.value.type == IR_VALUE_CONSTANT) { - // Must be U64 - DBGASSERT(addr.value.data.constant_d.type == IR_CONSTANT_U64); - insn.it = IMM64; - insn.imm64 = addr.value.data.constant_d.data.u64_d; - insn.opcode = BPF_IMM | size | BPF_LD; - } else { - CRITICAL("Error"); - } - return insn; -} - -void translate(struct ir_function *fun) { - struct ir_basic_block **pos; - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_insn *insn; - list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - struct ir_value v0 = insn->values[0]; - struct ir_value v1 = insn->values[1]; - enum val_type t0 = insn->value_num >= 1 ? vtype(v0) : UNDEF; - enum val_type t1 = insn->value_num >= 2 ? vtype(v1) : UNDEF; - enum val_type tdst = vtype_insn(insn); - struct ir_insn_cg_extra *extra = insn_cg(insn); - struct ir_insn *dst_insn = dst(insn); - if (insn->op == IR_INSN_ALLOC) { - // Nothing to do - extra->translated_num = 0; - } else if (insn->op == IR_INSN_STORE) { - CRITICAL("Error"); - } else if (insn->op == IR_INSN_LOAD) { - CRITICAL("Error"); - } else if (insn->op == IR_INSN_LOADRAW) { - DBGASSERT(tdst == REG); - } else if (insn->op == IR_INSN_STORERAW) { - } else if (insn->op >= IR_INSN_ADD && insn->op < IR_INSN_CALL) { - } else if (insn->op == IR_INSN_ASSIGN) { - } else if (insn->op == IR_INSN_RET) { - } else if (insn->op == IR_INSN_CALL) { - } else if (insn->op == IR_INSN_JA) { - } else if (insn->op >= IR_INSN_JEQ && insn->op < IR_INSN_PHI) { - } else { - CRITICAL("No such instruction"); - } - } - } -} diff --git a/IR/bpf_ir.c b/IR/bpf_ir.c index 26039329..ad0b9621 100644 --- a/IR/bpf_ir.c +++ b/IR/bpf_ir.c @@ -1,460 +1,494 @@ -#include "bpf_ir.h" -#include -#include -#include -#include -#include -#include "ir_helper.h" -#include "array.h" -#include "code_gen.h" -#include "ir_fun.h" -#include "list.h" -#include "dbg.h" -#include "passes.h" -#include "prog_check.h" -#include "read.h" +#include // TODO: Change this to real function -static __u32 helper_func_arg_num[100] = {1, 1, 1, 1, 1, 0, 2, 1, 1}; +static const __u32 helper_func_arg_num[100] = { 1, 1, 1, 1, 1, 0, 2, 1, 1 }; -int compare_num(const void *a, const void *b) { - struct bb_entrance_info *as = (struct bb_entrance_info *)a; - struct bb_entrance_info *bs = (struct bb_entrance_info *)b; - return as->entrance > bs->entrance; +// All function passes. +static const struct function_pass passes[] = { + DEF_FUNC_PASS(remove_trivial_phi, "Remove the trival Phi"), + DEF_FUNC_PASS(add_counter, "Adding counter"), +}; + +static void write_variable(struct ssa_transform_env *env, __u8 reg, + struct pre_ir_basic_block *bb, struct ir_value val); + +static struct ir_value read_variable(struct ssa_transform_env *env, __u8 reg, + struct pre_ir_basic_block *bb); + +static struct ir_insn *add_phi_operands(struct ssa_transform_env *env, __u8 reg, + struct ir_insn *insn); + +static void add_user(struct ssa_transform_env *env, struct ir_insn *user, + struct ir_value val); + +static int compare_num(const void *a, const void *b) +{ + struct bb_entrance_info *as = (struct bb_entrance_info *)a; + struct bb_entrance_info *bs = (struct bb_entrance_info *)b; + return as->entrance > bs->entrance; } // Add current_pos --> entrance_pos in bb_entrances -void add_entrance_info(struct bpf_insn *insns, struct array *bb_entrances, size_t entrance_pos, - size_t current_pos) { - for (size_t i = 0; i < bb_entrances->num_elem; ++i) { - struct bb_entrance_info *entry = ((struct bb_entrance_info *)(bb_entrances->data)) + i; - if (entry->entrance == entrance_pos) { - // Already has this entrance, add a pred - array_push_unique(&entry->bb->preds, ¤t_pos); - return; - } - } - // New entrance - struct array preds = array_init(sizeof(size_t)); - size_t last_pos = entrance_pos - 1; - __u8 code = insns[last_pos].code; - if (!(BPF_OP(code) == BPF_JA || BPF_OP(code) == BPF_EXIT)) { - // BPF_EXIT - array_push_unique(&preds, &last_pos); - } - array_push_unique(&preds, ¤t_pos); - struct bb_entrance_info new_bb; - new_bb.entrance = entrance_pos; - new_bb.bb = __malloc(sizeof(struct pre_ir_basic_block)); - new_bb.bb->preds = preds; - array_push(bb_entrances, &new_bb); +static int add_entrance_info(struct bpf_insn *insns, struct array *bb_entrances, + size_t entrance_pos, size_t current_pos) +{ + for (size_t i = 0; i < bb_entrances->num_elem; ++i) { + struct bb_entrance_info *entry = + ((struct bb_entrance_info *)(bb_entrances->data)) + i; + if (entry->entrance == entrance_pos) { + // Already has this entrance, add a pred + bpf_ir_array_push_unique(&entry->bb->preds, + ¤t_pos); + return 0; + } + } + // New entrance + struct array preds; + INIT_ARRAY(&preds, size_t); + size_t last_pos = entrance_pos - 1; + __u8 code = insns[last_pos].code; + if (!(BPF_OP(code) == BPF_JA || BPF_OP(code) == BPF_EXIT)) { + // BPF_EXIT + bpf_ir_array_push_unique(&preds, &last_pos); + } + bpf_ir_array_push_unique(&preds, ¤t_pos); + struct bb_entrance_info new_bb; + new_bb.entrance = entrance_pos; + SAFE_MALLOC(new_bb.bb, sizeof(struct pre_ir_basic_block)); + new_bb.bb->preds = preds; + bpf_ir_array_push(bb_entrances, &new_bb); + return 0; } // Return the parent BB of a instruction -struct pre_ir_basic_block *get_bb_parent(struct array *bb_entrance, size_t pos) { - size_t bb_id = 0; - struct bb_entrance_info *bbs = (struct bb_entrance_info *)(bb_entrance->data); - for (size_t i = 1; i < bb_entrance->num_elem; ++i) { - struct bb_entrance_info *entry = bbs + i; - if (entry->entrance <= pos) { - bb_id++; - } else { - break; - } - } - return bbs[bb_id].bb; -} - -void init_entrance_info(struct array *bb_entrances, size_t entrance_pos) { - for (size_t i = 0; i < bb_entrances->num_elem; ++i) { - struct bb_entrance_info *entry = ((struct bb_entrance_info *)(bb_entrances->data)) + i; - if (entry->entrance == entrance_pos) { - // Already has this entrance - return; - } - } - // New entrance - struct array preds = array_init(sizeof(size_t)); - struct bb_entrance_info new_bb; - new_bb.entrance = entrance_pos; - new_bb.bb = __malloc(sizeof(struct pre_ir_basic_block)); - new_bb.bb->preds = preds; - array_push(bb_entrances, &new_bb); -} - -struct ir_basic_block *init_ir_bb_raw() { - struct ir_basic_block *new_bb = __malloc(sizeof(struct ir_basic_block)); - INIT_LIST_HEAD(&new_bb->ir_insn_head); - new_bb->user_data = NULL; - new_bb->preds = INIT_ARRAY(struct ir_basic_block *); - new_bb->succs = INIT_ARRAY(struct ir_basic_block *); - new_bb->users = INIT_ARRAY(struct ir_insn *); - return new_bb; -} - -void init_ir_bb(struct pre_ir_basic_block *bb) { - bb->ir_bb = init_ir_bb_raw(); - bb->ir_bb->_visited = 0; - bb->ir_bb->user_data = bb; - for (__u8 i = 0; i < MAX_BPF_REG; ++i) { - bb->incompletePhis[i] = NULL; - } -} - -struct bb_info gen_bb(struct bpf_insn *insns, size_t len) { - struct array bb_entrance = array_init(sizeof(struct bb_entrance_info)); - // First, scan the code to find all the BB entrances - for (size_t i = 0; i < len; ++i) { - struct bpf_insn insn = insns[i]; - __u8 code = insn.code; - if (BPF_CLASS(code) == BPF_JMP || BPF_CLASS(code) == BPF_JMP32) { - if (i + 1 < len && insns[i + 1].code == 0) { - // TODO: What if insns[i+1] is a pseudo instruction? - CRITICAL("Error"); - } - if (BPF_OP(code) == BPF_JA) { - // Direct Jump - size_t pos = 0; - if (BPF_CLASS(code) == BPF_JMP) { - // JMP class (64 bits) - // TODO - // Add offset - pos = (__s16)i + insn.off + 1; - } else { - // JMP32 class - // TODO - // Add immediate - pos = (__s32)i + insn.imm + 1; - } - // Add to bb entrance - // This is one-way control flow - add_entrance_info(insns, &bb_entrance, pos, i); - } - if ((BPF_OP(code) >= BPF_JEQ && BPF_OP(code) <= BPF_JSGE) || - (BPF_OP(code) >= BPF_JLT && BPF_OP(code) <= BPF_JSLE)) { - // Add offset - size_t pos = (__s16)i + insn.off + 1; - add_entrance_info(insns, &bb_entrance, pos, i); - add_entrance_info(insns, &bb_entrance, i + 1, i); - } - if (BPF_OP(code) == BPF_CALL) { - // BPF_CALL - // Unsupported yet - continue; - } - if (BPF_OP(code) == BPF_EXIT) { - // BPF_EXIT - if (i + 1 < len) { - // Not the last instruction - init_entrance_info(&bb_entrance, i + 1); - } - } - } - } - - // Create the first BB (entry block) - struct bb_entrance_info bb_entry_info; - bb_entry_info.entrance = 0; - bb_entry_info.bb = __malloc(sizeof(struct pre_ir_basic_block)); - bb_entry_info.bb->preds = array_null(); - array_push(&bb_entrance, &bb_entry_info); - - // Sort the BBs - qsort(bb_entrance.data, bb_entrance.num_elem, bb_entrance.elem_size, &compare_num); - // Generate real basic blocks - - struct bb_entrance_info *all_bbs = ((struct bb_entrance_info *)(bb_entrance.data)); - - // Print the BB - // for (size_t i = 0; i < bb_entrance.num_elem; ++i) { - // struct bb_entrance_info entry = all_bbs[i]; - // printf("%ld: %ld\n", entry.entrance, entry.bb->preds.num_elem); - // } - - // Init preds - for (size_t i = 0; i < bb_entrance.num_elem; ++i) { - struct bb_entrance_info *entry = all_bbs + i; - struct pre_ir_basic_block *real_bb = entry->bb; - real_bb->id = i; - real_bb->succs = array_init(sizeof(struct pre_ir_basic_block *)); - real_bb->visited = 0; - real_bb->pre_insns = NULL; - real_bb->start_pos = entry->entrance; - real_bb->end_pos = i + 1 < bb_entrance.num_elem ? all_bbs[i + 1].entrance : len; - real_bb->filled = 0; - real_bb->sealed = 0; - real_bb->ir_bb = NULL; - } - - // Allocate instructions - for (size_t i = 0; i < bb_entrance.num_elem; ++i) { - struct pre_ir_basic_block *real_bb = all_bbs[i].bb; - real_bb->pre_insns = - __malloc(sizeof(struct pre_ir_insn) * (real_bb->end_pos - real_bb->start_pos)); - size_t bb_pos = 0; - for (size_t pos = real_bb->start_pos; pos < real_bb->end_pos; ++pos, ++bb_pos) { - struct bpf_insn insn = insns[pos]; - struct pre_ir_insn new_insn; - new_insn.opcode = insn.code; - new_insn.src_reg = insn.src_reg; - new_insn.dst_reg = insn.dst_reg; - new_insn.imm = insn.imm; - new_insn.it = IMM; - new_insn.imm64 = 0; - new_insn.off = insn.off; - new_insn.pos = pos; - if (pos + 1 < real_bb->end_pos && insns[pos + 1].code == 0) { - __u64 imml = (__u64)insn.imm & 0xFFFFFFFF; - new_insn.imm64 = ((__s64)(insns[pos + 1].imm) << 32) | imml; - new_insn.it = IMM64; - pos++; - } - real_bb->pre_insns[bb_pos] = new_insn; - } - real_bb->len = bb_pos; - } - for (size_t i = 0; i < bb_entrance.num_elem; ++i) { - struct bb_entrance_info *entry = all_bbs + i; - - struct array preds = entry->bb->preds; - struct array new_preds = array_init(sizeof(struct pre_ir_basic_block *)); - for (size_t j = 0; j < preds.num_elem; ++j) { - size_t pred_pos = ((size_t *)(preds.data))[j]; - // Get the real parent BB - struct pre_ir_basic_block *parent_bb = get_bb_parent(&bb_entrance, pred_pos); - // We push the address to the array - array_push(&new_preds, &parent_bb); - // Add entry->bb to the succ of parent_bb - array_push(&parent_bb->succs, &entry->bb); - } - array_free(&preds); - entry->bb->preds = new_preds; - } - // Return the entry BB - // TODO: Remove BBs impossible to reach - struct bb_info ret; - ret.entry = all_bbs[0].bb; - ret.all_bbs = bb_entrance; - return ret; -} - -void print_pre_ir_cfg(struct pre_ir_basic_block *bb) { - if (bb->visited) { - return; - } - bb->visited = 1; - printf("BB %ld:\n", bb->id); - for (size_t i = 0; i < bb->len; ++i) { - struct pre_ir_insn insn = bb->pre_insns[i]; - printf("%x %x %llx\n", insn.opcode, insn.imm, insn.imm64); - } - printf("preds (%ld): ", bb->preds.num_elem); - for (size_t i = 0; i < bb->preds.num_elem; ++i) { - struct pre_ir_basic_block *pred = ((struct pre_ir_basic_block **)(bb->preds.data))[i]; - printf("%ld ", pred->id); - } - printf("\nsuccs (%ld): ", bb->succs.num_elem); - for (size_t i = 0; i < bb->succs.num_elem; ++i) { - struct pre_ir_basic_block *succ = ((struct pre_ir_basic_block **)(bb->succs.data))[i]; - printf("%ld ", succ->id); - } - printf("\n\n"); - for (size_t i = 0; i < bb->succs.num_elem; ++i) { - struct pre_ir_basic_block *succ = ((struct pre_ir_basic_block **)(bb->succs.data))[i]; - print_pre_ir_cfg(succ); - } -} - -struct ssa_transform_env init_env(struct bb_info info) { - struct ssa_transform_env env; - for (size_t i = 0; i < MAX_BPF_REG; ++i) { - env.currentDef[i] = INIT_ARRAY(struct bb_val); - } - env.info = info; - env.sp_users = INIT_ARRAY(struct ir_insn *); - // Initialize function argument - for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { - env.function_arg[i] = __malloc(sizeof(struct ir_insn)); - - env.function_arg[i]->users = INIT_ARRAY(struct ir_insn *); - env.function_arg[i]->op = IR_INSN_FUNCTIONARG; - env.function_arg[i]->fid = i; - env.function_arg[i]->value_num = 0; - env.function_arg[i]->user_data = NULL; - struct ir_value val; - val.type = IR_VALUE_INSN; - val.data.insn_d = env.function_arg[i]; - write_variable(&env, BPF_REG_1 + i, info.entry, val); - } - return env; -} - -void seal_block(struct ssa_transform_env *env, struct pre_ir_basic_block *bb) { - // Seal a BB - for (__u8 i = 0; i < MAX_BPF_REG; ++i) { - if (bb->incompletePhis[i]) { - add_phi_operands(env, i, bb->incompletePhis[i]); - } - } - bb->sealed = 1; -} - -void write_variable(struct ssa_transform_env *env, __u8 reg, struct pre_ir_basic_block *bb, - struct ir_value val) { - if (reg >= MAX_BPF_REG - 1) { - // Stack pointer is read-only - CRITICAL("Error"); - } - // Write a variable to a BB - struct array *currentDef = &env->currentDef[reg]; - // Traverse the array to find if there exists a value in the same BB - for (size_t i = 0; i < currentDef->num_elem; ++i) { - struct bb_val *bval = ((struct bb_val *)(currentDef->data)) + i; - if (bval->bb == bb) { - // Found - bval->val = val; - return; - } - } - // Not found - struct bb_val new_val; - new_val.bb = bb; - new_val.val = val; - array_push(currentDef, &new_val); -} - -struct ir_insn *add_phi_operands(struct ssa_transform_env *env, __u8 reg, struct ir_insn *insn) { - // insn must be a (initialized) PHI instruction - if (insn->op != IR_INSN_PHI) { - CRITICAL("Not a PHI node"); - } - for (size_t i = 0; i < insn->parent_bb->preds.num_elem; ++i) { - struct ir_basic_block *pred = ((struct ir_basic_block **)(insn->parent_bb->preds.data))[i]; - struct phi_value phi; - phi.bb = pred; - phi.value = read_variable(env, reg, (struct pre_ir_basic_block *)pred->user_data); - add_user(env, insn, phi.value); - array_push(&pred->users, &insn); - array_push(&insn->phi, &phi); - } - return insn; -} - -struct ir_value read_variable_recursive(struct ssa_transform_env *env, __u8 reg, - struct pre_ir_basic_block *bb) { - struct ir_value val; - if (!bb->sealed) { - // Incomplete CFG - struct ir_insn *new_insn = create_insn_front(bb->ir_bb); - new_insn->op = IR_INSN_PHI; - new_insn->phi = INIT_ARRAY(struct phi_value); - bb->incompletePhis[reg] = new_insn; - val.type = IR_VALUE_INSN; - val.data.insn_d = new_insn; - } else if (bb->preds.num_elem == 1) { - val = read_variable(env, reg, ((struct pre_ir_basic_block **)(bb->preds.data))[0]); - } else { - struct ir_insn *new_insn = create_insn_front(bb->ir_bb); - new_insn->op = IR_INSN_PHI; - new_insn->phi = INIT_ARRAY(struct phi_value); - val.type = IR_VALUE_INSN; - val.data.insn_d = new_insn; - write_variable(env, reg, bb, val); - new_insn = add_phi_operands(env, reg, new_insn); - val.type = IR_VALUE_INSN; - val.data.insn_d = new_insn; - } - write_variable(env, reg, bb, val); - return val; -} - -struct ir_value read_variable(struct ssa_transform_env *env, __u8 reg, - struct pre_ir_basic_block *bb) { - // Read a variable from a BB - if (reg == BPF_REG_10) { - // Stack pointer - struct ir_value val; - val.type = IR_VALUE_STACK_PTR; - return val; - } - struct array *currentDef = &env->currentDef[reg]; - for (size_t i = 0; i < currentDef->num_elem; ++i) { - struct bb_val *bval = ((struct bb_val *)(currentDef->data)) + i; - if (bval->bb == bb) { - // Found - return bval->val; - } - } - // Not found - return read_variable_recursive(env, reg, bb); -} - -struct ir_insn *create_insn() { - struct ir_insn *insn = __malloc(sizeof(struct ir_insn)); - insn->users = INIT_ARRAY(struct ir_insn *); - return insn; -} - -struct ir_insn *create_insn_back(struct ir_basic_block *bb) { - struct ir_insn *insn = create_insn(); - insn->parent_bb = bb; - list_add_tail(&insn->list_ptr, &bb->ir_insn_head); - return insn; -} - -struct ir_insn *create_insn_front(struct ir_basic_block *bb) { - struct ir_insn *insn = create_insn(); - insn->parent_bb = bb; - list_add(&insn->list_ptr, &bb->ir_insn_head); - return insn; -} - -enum ir_vr_type to_ir_ld_u(__u8 size) { - switch (size) { - case BPF_W: - return IR_VR_TYPE_32; - case BPF_H: - return IR_VR_TYPE_16; - case BPF_B: - return IR_VR_TYPE_8; - case BPF_DW: - return IR_VR_TYPE_64; - default: - CRITICAL("Error"); - } -} - -int vr_type_to_size(enum ir_vr_type type) { - switch (type) { - case IR_VR_TYPE_32: - return BPF_W; - case IR_VR_TYPE_16: - return BPF_H; - case IR_VR_TYPE_8: - return BPF_B; - case IR_VR_TYPE_64: - return BPF_DW; - default: - CRITICAL("Error"); - } -} - -struct ir_value ir_value_insn(struct ir_insn *insn) { - return (struct ir_value){.type = IR_VALUE_INSN, .data.insn_d = insn}; -} - -struct ir_value ir_value_stack_ptr() { - return (struct ir_value){.type = IR_VALUE_STACK_PTR}; +static struct pre_ir_basic_block *get_bb_parent(struct array *bb_entrance, + size_t pos) +{ + size_t bb_id = 0; + struct bb_entrance_info *bbs = + (struct bb_entrance_info *)(bb_entrance->data); + for (size_t i = 1; i < bb_entrance->num_elem; ++i) { + struct bb_entrance_info *entry = bbs + i; + if (entry->entrance <= pos) { + bb_id++; + } else { + break; + } + } + return bbs[bb_id].bb; +} + +static int init_entrance_info(struct array *bb_entrances, size_t entrance_pos) +{ + for (size_t i = 0; i < bb_entrances->num_elem; ++i) { + struct bb_entrance_info *entry = + ((struct bb_entrance_info *)(bb_entrances->data)) + i; + if (entry->entrance == entrance_pos) { + // Already has this entrance + return 0; + } + } + // New entrance + struct array preds; + INIT_ARRAY(&preds, size_t); + struct bb_entrance_info new_bb; + new_bb.entrance = entrance_pos; + SAFE_MALLOC(new_bb.bb, sizeof(struct pre_ir_basic_block)); + new_bb.bb->preds = preds; + bpf_ir_array_push(bb_entrances, &new_bb); + return 0; +} + +static int init_ir_bb(struct pre_ir_basic_block *bb) +{ + bb->ir_bb = bpf_ir_init_bb_raw(); + if (!bb->ir_bb) { + return -ENOMEM; + } + bb->ir_bb->_visited = 0; + bb->ir_bb->user_data = bb; + for (__u8 i = 0; i < MAX_BPF_REG; ++i) { + bb->incompletePhis[i] = NULL; + } + return 0; +} + +static int gen_bb(struct bb_info *ret, struct bpf_insn *insns, size_t len) +{ + struct array bb_entrance; + INIT_ARRAY(&bb_entrance, struct bb_entrance_info); + // First, scan the code to find all the BB entrances + for (size_t i = 0; i < len; ++i) { + struct bpf_insn insn = insns[i]; + __u8 code = insn.code; + if (BPF_CLASS(code) == BPF_JMP || + BPF_CLASS(code) == BPF_JMP32) { + if (i + 1 < len && insns[i + 1].code == 0) { + // TODO: What if insns[i+1] is a pseudo instruction? + CRITICAL("Error"); + } + if (BPF_OP(code) == BPF_JA) { + // Direct Jump + size_t pos = 0; + if (BPF_CLASS(code) == BPF_JMP) { + // JMP class (64 bits) + // TODO + // Add offset + pos = (__s16)i + insn.off + 1; + } else { + // JMP32 class + // TODO + // Add immediate + pos = (__s32)i + insn.imm + 1; + } + // Add to bb entrance + // This is one-way control flow + add_entrance_info(insns, &bb_entrance, pos, i); + } + if ((BPF_OP(code) >= BPF_JEQ && + BPF_OP(code) <= BPF_JSGE) || + (BPF_OP(code) >= BPF_JLT && + BPF_OP(code) <= BPF_JSLE)) { + // Add offset + size_t pos = (__s16)i + insn.off + 1; + add_entrance_info(insns, &bb_entrance, pos, i); + add_entrance_info(insns, &bb_entrance, i + 1, + i); + } + if (BPF_OP(code) == BPF_CALL) { + // BPF_CALL + // Unsupported yet + continue; + } + if (BPF_OP(code) == BPF_EXIT) { + // BPF_EXIT + if (i + 1 < len) { + // Not the last instruction + init_entrance_info(&bb_entrance, i + 1); + } + } + } + } + + // Create the first BB (entry block) + struct bb_entrance_info bb_entry_info; + bb_entry_info.entrance = 0; + SAFE_MALLOC(bb_entry_info.bb, sizeof(struct pre_ir_basic_block)); + bb_entry_info.bb->preds = bpf_ir_array_null(); + bpf_ir_array_push(&bb_entrance, &bb_entry_info); + + // Sort the BBs + qsort(bb_entrance.data, bb_entrance.num_elem, bb_entrance.elem_size, + &compare_num); + // Generate real basic blocks + + struct bb_entrance_info *all_bbs = + ((struct bb_entrance_info *)(bb_entrance.data)); + + // Print the BB + // for (size_t i = 0; i < bb_entrance.num_elem; ++i) { + // struct bb_entrance_info entry = all_bbs[i]; + // PRINT_LOG("%ld: %ld\n", entry.entrance, entry.bb->preds.num_elem); + // } + + // Init preds + for (size_t i = 0; i < bb_entrance.num_elem; ++i) { + struct bb_entrance_info *entry = all_bbs + i; + struct pre_ir_basic_block *real_bb = entry->bb; + real_bb->id = i; + INIT_ARRAY(&real_bb->succs, struct pre_ir_basic_block *); + real_bb->visited = 0; + real_bb->pre_insns = NULL; + real_bb->start_pos = entry->entrance; + real_bb->end_pos = i + 1 < bb_entrance.num_elem ? + all_bbs[i + 1].entrance : + len; + real_bb->filled = 0; + real_bb->sealed = 0; + real_bb->ir_bb = NULL; + } + + // Allocate instructions + for (size_t i = 0; i < bb_entrance.num_elem; ++i) { + struct pre_ir_basic_block *real_bb = all_bbs[i].bb; + SAFE_MALLOC(real_bb->pre_insns, + sizeof(struct pre_ir_insn) * + (real_bb->end_pos - real_bb->start_pos)); + size_t bb_pos = 0; + for (size_t pos = real_bb->start_pos; pos < real_bb->end_pos; + ++pos, ++bb_pos) { + struct bpf_insn insn = insns[pos]; + struct pre_ir_insn new_insn; + new_insn.opcode = insn.code; + new_insn.src_reg = insn.src_reg; + new_insn.dst_reg = insn.dst_reg; + new_insn.imm = insn.imm; + new_insn.it = IMM; + new_insn.imm64 = 0; + new_insn.off = insn.off; + new_insn.pos = pos; + if (pos + 1 < real_bb->end_pos && + insns[pos + 1].code == 0) { + __u64 imml = (__u64)insn.imm & 0xFFFFFFFF; + new_insn.imm64 = + ((__s64)(insns[pos + 1].imm) << 32) | + imml; + new_insn.it = IMM64; + pos++; + } + real_bb->pre_insns[bb_pos] = new_insn; + } + real_bb->len = bb_pos; + } + for (size_t i = 0; i < bb_entrance.num_elem; ++i) { + struct bb_entrance_info *entry = all_bbs + i; + + struct array preds = entry->bb->preds; + struct array new_preds; + INIT_ARRAY(&new_preds, struct pre_ir_basic_block *); + for (size_t j = 0; j < preds.num_elem; ++j) { + size_t pred_pos = ((size_t *)(preds.data))[j]; + // Get the real parent BB + struct pre_ir_basic_block *parent_bb = + get_bb_parent(&bb_entrance, pred_pos); + // We push the address to the array + bpf_ir_array_push(&new_preds, &parent_bb); + // Add entry->bb to the succ of parent_bb + bpf_ir_array_push(&parent_bb->succs, &entry->bb); + } + bpf_ir_array_free(&preds); + entry->bb->preds = new_preds; + } + // Return the entry BB + ret->entry = all_bbs[0].bb; + ret->all_bbs = bb_entrance; + return 0; +} + +static void print_pre_ir_cfg(struct pre_ir_basic_block *bb) +{ + if (bb->visited) { + return; + } + bb->visited = 1; + PRINT_LOG("BB %ld:\n", bb->id); + for (size_t i = 0; i < bb->len; ++i) { + struct pre_ir_insn insn = bb->pre_insns[i]; + PRINT_LOG("%x %x %llx\n", insn.opcode, insn.imm, insn.imm64); + } + PRINT_LOG("preds (%ld): ", bb->preds.num_elem); + for (size_t i = 0; i < bb->preds.num_elem; ++i) { + struct pre_ir_basic_block *pred = + ((struct pre_ir_basic_block **)(bb->preds.data))[i]; + PRINT_LOG("%ld ", pred->id); + } + PRINT_LOG("\nsuccs (%ld): ", bb->succs.num_elem); + for (size_t i = 0; i < bb->succs.num_elem; ++i) { + struct pre_ir_basic_block *succ = + ((struct pre_ir_basic_block **)(bb->succs.data))[i]; + PRINT_LOG("%ld ", succ->id); + } + PRINT_LOG("\n\n"); + for (size_t i = 0; i < bb->succs.num_elem; ++i) { + struct pre_ir_basic_block *succ = + ((struct pre_ir_basic_block **)(bb->succs.data))[i]; + print_pre_ir_cfg(succ); + } +} + +static int init_env(struct ssa_transform_env *env, struct bb_info info) +{ + for (size_t i = 0; i < MAX_BPF_REG; ++i) { + INIT_ARRAY(&env->currentDef[i], struct bb_val); + } + env->info = info; + INIT_ARRAY(&env->sp_users, struct ir_insn *); + // Initialize function argument + for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { + SAFE_MALLOC(env->function_arg[i], sizeof(struct ir_insn)); + + INIT_ARRAY(&env->function_arg[i]->users, struct ir_insn *); + env->function_arg[i]->op = IR_INSN_FUNCTIONARG; + env->function_arg[i]->fid = i; + env->function_arg[i]->value_num = 0; + env->function_arg[i]->user_data = NULL; + struct ir_value val; + val.type = IR_VALUE_INSN; + val.data.insn_d = env->function_arg[i]; + write_variable(env, BPF_REG_1 + i, info.entry, val); + } + return 0; +} + +static void seal_block(struct ssa_transform_env *env, + struct pre_ir_basic_block *bb) +{ + // Seal a BB + for (__u8 i = 0; i < MAX_BPF_REG; ++i) { + if (bb->incompletePhis[i]) { + add_phi_operands(env, i, bb->incompletePhis[i]); + } + } + bb->sealed = 1; +} + +static void write_variable(struct ssa_transform_env *env, __u8 reg, + struct pre_ir_basic_block *bb, struct ir_value val) +{ + if (reg >= MAX_BPF_REG - 1) { + // Stack pointer is read-only + CRITICAL("Error"); + } + // Write a variable to a BB + struct array *currentDef = &env->currentDef[reg]; + // Traverse the array to find if there exists a value in the same BB + for (size_t i = 0; i < currentDef->num_elem; ++i) { + struct bb_val *bval = ((struct bb_val *)(currentDef->data)) + i; + if (bval->bb == bb) { + // Found + bval->val = val; + return; + } + } + // Not found + struct bb_val new_val; + new_val.bb = bb; + new_val.val = val; + bpf_ir_array_push(currentDef, &new_val); +} + +static struct ir_insn *add_phi_operands(struct ssa_transform_env *env, __u8 reg, + struct ir_insn *insn) +{ + // insn must be a (initialized) PHI instruction + if (insn->op != IR_INSN_PHI) { + CRITICAL("Not a PHI node"); + } + for (size_t i = 0; i < insn->parent_bb->preds.num_elem; ++i) { + struct ir_basic_block *pred = + ((struct ir_basic_block **)(insn->parent_bb->preds + .data))[i]; + struct phi_value phi; + phi.bb = pred; + phi.value = read_variable( + env, reg, (struct pre_ir_basic_block *)pred->user_data); + add_user(env, insn, phi.value); + bpf_ir_array_push(&pred->users, &insn); + bpf_ir_array_push(&insn->phi, &phi); + } + return insn; +} + +static struct ir_insn *create_insn(void) +{ + struct ir_insn *insn = malloc_proto(sizeof(struct ir_insn)); + if (!insn) { + return NULL; + } + INIT_ARRAY(&insn->users, struct ir_insn *); + // Setting the default values + insn->alu = IR_ALU_UNKNOWN; + insn->vr_type = IR_VR_TYPE_UNKNOWN; + insn->value_num = 0; + return insn; +} + +static struct ir_insn *create_insn_back(struct ir_basic_block *bb) +{ + struct ir_insn *insn = create_insn(); + insn->parent_bb = bb; + list_add_tail(&insn->list_ptr, &bb->ir_insn_head); + return insn; +} + +static struct ir_insn *create_insn_front(struct ir_basic_block *bb) +{ + struct ir_insn *insn = create_insn(); + insn->parent_bb = bb; + list_add(&insn->list_ptr, &bb->ir_insn_head); + return insn; +} + +static struct ir_value read_variable_recursive(struct ssa_transform_env *env, + __u8 reg, + struct pre_ir_basic_block *bb) +{ + struct ir_value val; + if (!bb->sealed) { + // Incomplete CFG + struct ir_insn *new_insn = create_insn_front(bb->ir_bb); + new_insn->op = IR_INSN_PHI; + INIT_ARRAY(&new_insn->phi, struct phi_value); + bb->incompletePhis[reg] = new_insn; + val.type = IR_VALUE_INSN; + val.data.insn_d = new_insn; + } else if (bb->preds.num_elem == 1) { + val = read_variable( + env, reg, + ((struct pre_ir_basic_block **)(bb->preds.data))[0]); + } else { + struct ir_insn *new_insn = create_insn_front(bb->ir_bb); + new_insn->op = IR_INSN_PHI; + INIT_ARRAY(&new_insn->phi, struct phi_value); + val.type = IR_VALUE_INSN; + val.data.insn_d = new_insn; + write_variable(env, reg, bb, val); + new_insn = add_phi_operands(env, reg, new_insn); + val.type = IR_VALUE_INSN; + val.data.insn_d = new_insn; + } + write_variable(env, reg, bb, val); + return val; +} + +static struct ir_value read_variable(struct ssa_transform_env *env, __u8 reg, + struct pre_ir_basic_block *bb) +{ + // Read a variable from a BB + if (reg == BPF_REG_10) { + // Stack pointer + struct ir_value val; + val.type = IR_VALUE_STACK_PTR; + return val; + } + struct array *currentDef = &env->currentDef[reg]; + for (size_t i = 0; i < currentDef->num_elem; ++i) { + struct bb_val *bval = ((struct bb_val *)(currentDef->data)) + i; + if (bval->bb == bb) { + // Found + return bval->val; + } + } + // Not found + return read_variable_recursive(env, reg, bb); +} + +static enum ir_vr_type to_ir_ld_u(__u8 size) +{ + switch (size) { + case BPF_W: + return IR_VR_TYPE_32; + case BPF_H: + return IR_VR_TYPE_16; + case BPF_B: + return IR_VR_TYPE_8; + case BPF_DW: + return IR_VR_TYPE_64; + default: + CRITICAL("Error"); + } } // User uses val -void add_user(struct ssa_transform_env *env, struct ir_insn *user, struct ir_value val) { - if (val.type == IR_VALUE_INSN) { - array_push_unique(&val.data.insn_d->users, &user); - } - if (val.type == IR_VALUE_STACK_PTR) { - array_push_unique(&env->sp_users, &user); - } +static void add_user(struct ssa_transform_env *env, struct ir_insn *user, + struct ir_value val) +{ + if (val.type == IR_VALUE_INSN) { + bpf_ir_array_push_unique(&val.data.insn_d->users, &user); + } + if (val.type == IR_VALUE_STACK_PTR) { + bpf_ir_array_push_unique(&env->sp_users, &user); + } } /** @@ -462,454 +496,591 @@ void add_user(struct ssa_transform_env *env, struct ir_insn *user, struct ir_val Allocate memory and set the preds and succs. */ -void init_ir_bbs(struct ssa_transform_env *env) { - for (size_t i = 0; i < env->info.all_bbs.num_elem; ++i) { - struct pre_ir_basic_block *bb = ((struct bb_entrance_info *)(env->info.all_bbs.data))[i].bb; - init_ir_bb(bb); - } - // Set the preds and succs - for (size_t i = 0; i < env->info.all_bbs.num_elem; ++i) { - struct pre_ir_basic_block *bb = ((struct bb_entrance_info *)(env->info.all_bbs.data))[i].bb; - struct ir_basic_block *irbb = bb->ir_bb; - for (size_t j = 0; j < bb->preds.num_elem; ++j) { - struct pre_ir_basic_block *pred = ((struct pre_ir_basic_block **)(bb->preds.data))[j]; - array_push(&irbb->preds, &pred->ir_bb); - } - for (size_t j = 0; j < bb->succs.num_elem; ++j) { - struct pre_ir_basic_block *succ = ((struct pre_ir_basic_block **)(bb->succs.data))[j]; - array_push(&irbb->succs, &succ->ir_bb); - } - } -} - -struct ir_basic_block *get_ir_bb_from_position(struct ssa_transform_env *env, size_t pos) { - // Iterate through all the BBs - for (size_t i = 0; i < env->info.all_bbs.num_elem; ++i) { - struct bb_entrance_info *info = &((struct bb_entrance_info *)(env->info.all_bbs.data))[i]; - if (info->entrance == pos) { - return info->bb->ir_bb; - } - } - CRITICAL("Error"); -} - -struct ir_value get_src_value(struct ssa_transform_env *env, struct pre_ir_basic_block *bb, - struct pre_ir_insn insn) { - __u8 code = insn.opcode; - if (BPF_SRC(code) == BPF_K) { - struct ir_constant c; - c.data.s32_d = insn.imm; - c.type = IR_CONSTANT_S32; - return (struct ir_value){.type = IR_VALUE_CONSTANT, .data.constant_d = c}; - } else if (BPF_SRC(code) == BPF_X) { - return read_variable(env, insn.src_reg, bb); - } else { - CRITICAL("Error"); - } -} -struct ir_insn *create_alu_bin(struct ir_basic_block *bb, struct ir_value val1, - struct ir_value val2, enum ir_insn_type ty, - struct ssa_transform_env *env) { - struct ir_insn *new_insn = create_insn_back(bb); - new_insn->op = ty; - new_insn->values[0] = val1; - new_insn->values[1] = val2; - new_insn->value_num = 2; - add_user(env, new_insn, new_insn->values[0]); - add_user(env, new_insn, new_insn->values[1]); - return new_insn; -} - -void alu_write(struct ssa_transform_env *env, enum ir_insn_type ty, struct pre_ir_insn insn, - struct pre_ir_basic_block *bb) { - struct ir_insn *new_insn = create_alu_bin(bb->ir_bb, read_variable(env, insn.dst_reg, bb), - get_src_value(env, bb, insn), ty, env); - struct ir_value new_val; - new_val.type = IR_VALUE_INSN; - new_val.data.insn_d = new_insn; - write_variable(env, insn.dst_reg, bb, new_val); -} - -void transform_bb(struct ssa_transform_env *env, struct pre_ir_basic_block *bb) { - printf("Transforming BB%zu\n", bb->id); - if (bb->sealed) { - return; - } - // Try sealing a BB - __u8 pred_all_filled = 1; - for (size_t i = 0; i < bb->preds.num_elem; ++i) { - struct pre_ir_basic_block *pred = ((struct pre_ir_basic_block **)(bb->preds.data))[i]; - if (!pred->filled) { - // Not filled - pred_all_filled = 0; - break; - } - } - if (pred_all_filled) { - seal_block(env, bb); - } - if (bb->filled) { - // Already visited (filled) - return; - } - // Fill the BB - for (size_t i = 0; i < bb->len; ++i) { - struct pre_ir_insn insn = bb->pre_insns[i]; - __u8 code = insn.opcode; - if (BPF_CLASS(code) == BPF_ALU || BPF_CLASS(code) == BPF_ALU64) { - // 32-bit ALU class - // TODO: 64-bit ALU class - if (BPF_OP(code) == BPF_ADD) { - alu_write(env, IR_INSN_ADD, insn, bb); - } else if (BPF_OP(code) == BPF_SUB) { - alu_write(env, IR_INSN_SUB, insn, bb); - } else if (BPF_OP(code) == BPF_MUL) { - alu_write(env, IR_INSN_MUL, insn, bb); - } else if (BPF_OP(code) == BPF_MOV) { - // Do not create instructions - write_variable(env, insn.dst_reg, bb, get_src_value(env, bb, insn)); - } else if (BPF_OP(code) == BPF_LSH) { - alu_write(env, IR_INSN_LSH, insn, bb); - } else if (BPF_OP(code) == BPF_MOD) { - // dst = (src != 0) ? (dst % src) : dst - alu_write(env, IR_INSN_MOD, insn, bb); - } - - else { - // TODO - CRITICAL("Error"); - } - - } else if (BPF_CLASS(code) == BPF_LD && BPF_MODE(code) == BPF_IMM && - BPF_SIZE(code) == BPF_DW) { - // 64-bit immediate load - if (insn.src_reg == 0x0) { - // immediate value - struct ir_value imm_val; - imm_val.type = IR_VALUE_CONSTANT; - imm_val.data.constant_d.type = IR_CONSTANT_U64; - imm_val.data.constant_d.data.u64_d = insn.imm64; - write_variable(env, insn.dst_reg, bb, imm_val); - } else { - CRITICAL("Not supported"); - } - } else if (BPF_CLASS(code) == BPF_LDX && BPF_MODE(code) == BPF_MEMSX) { - // dst = *(signed size *) (src + offset) - // https://www.kernel.org/doc/html/v6.6/bpf/standardization/instruction-set.html#sign-extension-load-operations - - struct ir_insn *new_insn = create_insn_back(bb->ir_bb); - new_insn->op = IR_INSN_LOADRAW; - struct ir_address_value addr_val; - addr_val.value = read_variable(env, insn.src_reg, bb); - add_user(env, new_insn, addr_val.value); - addr_val.offset = insn.off; - new_insn->vr_type = to_ir_ld_u(BPF_SIZE(code)); - new_insn->addr_val = addr_val; - - struct ir_value new_val; - new_val.type = IR_VALUE_INSN; - new_val.data.insn_d = new_insn; - write_variable(env, insn.dst_reg, bb, new_val); - } else if (BPF_CLASS(code) == BPF_LDX && BPF_MODE(code) == BPF_MEM) { - // Regular load - // dst = *(unsigned size *) (src + offset) - // https://www.kernel.org/doc/html/v6.6/bpf/standardization/instruction-set.html#regular-load-and-store-operations - // TODO: use LOAD instead of LOADRAW - struct ir_insn *new_insn = create_insn_back(bb->ir_bb); - new_insn->op = IR_INSN_LOADRAW; - struct ir_address_value addr_val; - addr_val.value = read_variable(env, insn.src_reg, bb); - add_user(env, new_insn, addr_val.value); - addr_val.offset = insn.off; - new_insn->vr_type = to_ir_ld_u(BPF_SIZE(code)); - new_insn->addr_val = addr_val; - - struct ir_value new_val; - new_val.type = IR_VALUE_INSN; - new_val.data.insn_d = new_insn; - write_variable(env, insn.dst_reg, bb, new_val); - } else if (BPF_CLASS(code) == BPF_ST && BPF_MODE(code) == BPF_MEM) { - // *(size *) (dst + offset) = imm32 - struct ir_insn *new_insn = create_insn_back(bb->ir_bb); - new_insn->op = IR_INSN_STORERAW; - struct ir_address_value addr_val; - addr_val.value = read_variable(env, insn.dst_reg, bb); - add_user(env, new_insn, addr_val.value); - addr_val.offset = insn.off; - new_insn->vr_type = to_ir_ld_u(BPF_SIZE(code)); - new_insn->addr_val = addr_val; - new_insn->values[0].type = IR_VALUE_CONSTANT; - new_insn->values[0].data.constant_d.type = IR_CONSTANT_S32; - new_insn->values[0].data.constant_d.data.s32_d = insn.imm; - new_insn->value_num = 1; - } else if (BPF_CLASS(code) == BPF_STX && BPF_MODE(code) == BPF_MEM) { - // *(size *) (dst + offset) = src - struct ir_insn *new_insn = create_insn_back(bb->ir_bb); - new_insn->op = IR_INSN_STORERAW; - struct ir_address_value addr_val; - addr_val.value = read_variable(env, insn.dst_reg, bb); - add_user(env, new_insn, addr_val.value); - addr_val.offset = insn.off; - new_insn->vr_type = to_ir_ld_u(BPF_SIZE(code)); - new_insn->addr_val = addr_val; - new_insn->values[0] = read_variable(env, insn.src_reg, bb); - new_insn->value_num = 1; - add_user(env, new_insn, new_insn->values[0]); - } else if (BPF_CLASS(code) == BPF_JMP) { - if (BPF_OP(code) == BPF_JA) { - // Direct Jump - // PC += offset - struct ir_insn *new_insn = create_insn_back(bb->ir_bb); - new_insn->op = IR_INSN_JA; - size_t pos = insn.pos + insn.off + 1; - new_insn->bb1 = get_ir_bb_from_position(env, pos); - array_push(&new_insn->bb1->users, &new_insn); - } else if (BPF_OP(code) == BPF_EXIT) { - // Exit - struct ir_insn *new_insn = create_insn_back(bb->ir_bb); - new_insn->op = IR_INSN_RET; - new_insn->values[0] = read_variable(env, BPF_REG_0, bb); - new_insn->value_num = 1; - } else if (BPF_OP(code) == BPF_JEQ) { - // PC += offset if dst == src - struct ir_insn *new_insn = create_insn_back(bb->ir_bb); - new_insn->op = IR_INSN_JEQ; - new_insn->values[0] = read_variable(env, insn.dst_reg, bb); - new_insn->values[1] = get_src_value(env, bb, insn); - new_insn->value_num = 2; - add_user(env, new_insn, new_insn->values[0]); - add_user(env, new_insn, new_insn->values[1]); - size_t pos = insn.pos + insn.off + 1; - new_insn->bb1 = get_ir_bb_from_position(env, insn.pos + 1); - new_insn->bb2 = get_ir_bb_from_position(env, pos); - array_push(&new_insn->bb1->users, &new_insn); - array_push(&new_insn->bb2->users, &new_insn); - } else if (BPF_OP(code) == BPF_JLT) { - // PC += offset if dst < src - struct ir_insn *new_insn = create_insn_back(bb->ir_bb); - new_insn->op = IR_INSN_JLT; - new_insn->values[0] = read_variable(env, insn.dst_reg, bb); - new_insn->values[1] = get_src_value(env, bb, insn); - new_insn->value_num = 2; - add_user(env, new_insn, new_insn->values[0]); - add_user(env, new_insn, new_insn->values[1]); - size_t pos = insn.pos + insn.off + 1; - new_insn->bb1 = get_ir_bb_from_position(env, insn.pos + 1); - new_insn->bb2 = get_ir_bb_from_position(env, pos); - array_push(&new_insn->bb1->users, &new_insn); - array_push(&new_insn->bb2->users, &new_insn); - } else if (BPF_OP(code) == BPF_JLE) { - // PC += offset if dst <= src - struct ir_insn *new_insn = create_insn_back(bb->ir_bb); - new_insn->op = IR_INSN_JLE; - new_insn->values[0] = read_variable(env, insn.dst_reg, bb); - new_insn->values[1] = get_src_value(env, bb, insn); - new_insn->value_num = 2; - add_user(env, new_insn, new_insn->values[0]); - add_user(env, new_insn, new_insn->values[1]); - size_t pos = insn.pos + insn.off + 1; - new_insn->bb1 = get_ir_bb_from_position(env, insn.pos + 1); - new_insn->bb2 = get_ir_bb_from_position(env, pos); - array_push(&new_insn->bb1->users, &new_insn); - array_push(&new_insn->bb2->users, &new_insn); - } else if (BPF_OP(code) == BPF_JGT) { - // PC += offset if dst > src - struct ir_insn *new_insn = create_insn_back(bb->ir_bb); - new_insn->op = IR_INSN_JGT; - new_insn->values[0] = read_variable(env, insn.dst_reg, bb); - new_insn->values[1] = get_src_value(env, bb, insn); - new_insn->value_num = 2; - add_user(env, new_insn, new_insn->values[0]); - add_user(env, new_insn, new_insn->values[1]); - size_t pos = insn.pos + insn.off + 1; - new_insn->bb1 = get_ir_bb_from_position(env, insn.pos + 1); - new_insn->bb2 = get_ir_bb_from_position(env, pos); - array_push(&new_insn->bb1->users, &new_insn); - array_push(&new_insn->bb2->users, &new_insn); - } else if (BPF_OP(code) == BPF_JGE) { - // PC += offset if dst >= src - struct ir_insn *new_insn = create_insn_back(bb->ir_bb); - new_insn->op = IR_INSN_JGE; - new_insn->values[0] = read_variable(env, insn.dst_reg, bb); - new_insn->values[1] = get_src_value(env, bb, insn); - new_insn->value_num = 2; - add_user(env, new_insn, new_insn->values[0]); - add_user(env, new_insn, new_insn->values[1]); - size_t pos = insn.pos + insn.off + 1; - new_insn->bb1 = get_ir_bb_from_position(env, insn.pos + 1); - new_insn->bb2 = get_ir_bb_from_position(env, pos); - array_push(&new_insn->bb1->users, &new_insn); - array_push(&new_insn->bb2->users, &new_insn); - } else if (BPF_OP(code) == BPF_JNE) { - // PC += offset if dst != src - struct ir_insn *new_insn = create_insn_back(bb->ir_bb); - new_insn->op = IR_INSN_JNE; - new_insn->values[0] = read_variable(env, insn.dst_reg, bb); - new_insn->values[1] = get_src_value(env, bb, insn); - new_insn->value_num = 2; - add_user(env, new_insn, new_insn->values[0]); - add_user(env, new_insn, new_insn->values[1]); - size_t pos = insn.pos + insn.off + 1; - new_insn->bb1 = get_ir_bb_from_position(env, insn.pos + 1); - new_insn->bb2 = get_ir_bb_from_position(env, pos); - array_push(&new_insn->bb1->users, &new_insn); - array_push(&new_insn->bb2->users, &new_insn); - } else if (BPF_OP(code) == BPF_CALL) { - // imm is the function id - struct ir_insn *new_insn = create_insn_back(bb->ir_bb); - new_insn->op = IR_INSN_CALL; - new_insn->fid = insn.imm; - if (insn.imm < 0) { - printf("Not supported function call\n"); - new_insn->value_num = 0; - } else { - new_insn->value_num = helper_func_arg_num[insn.imm]; - if (new_insn->value_num > MAX_FUNC_ARG) { - CRITICAL("Too many arguments"); - } - for (size_t j = 0; j < new_insn->value_num; ++j) { - new_insn->values[j] = read_variable(env, BPF_REG_1 + j, bb); - add_user(env, new_insn, new_insn->values[j]); - } - } - - struct ir_value new_val; - new_val.type = IR_VALUE_INSN; - new_val.data.insn_d = new_insn; - write_variable(env, BPF_REG_0, bb, new_val); - } else { - // TODO - CRITICAL("Error"); - } - } else { - // TODO - printf("Class 0x%02x not supported\n", BPF_CLASS(code)); - CRITICAL("Error"); - } - } - bb->filled = 1; - // Finish filling - for (size_t i = 0; i < bb->succs.num_elem; ++i) { - struct pre_ir_basic_block *succ = ((struct pre_ir_basic_block **)(bb->succs.data))[i]; - transform_bb(env, succ); - } -} - -void free_function(struct ir_function *fun) { - array_free(&fun->sp_users); - for (size_t i = 0; i < fun->all_bbs.num_elem; ++i) { - struct ir_basic_block *bb = ((struct ir_basic_block **)(fun->all_bbs.data))[i]; - - array_free(&bb->preds); - array_free(&bb->succs); - array_free(&bb->users); - // Free the instructions - struct ir_insn *pos, *n; - list_for_each_entry_safe(pos, n, &bb->ir_insn_head, list_ptr) { - list_del(&pos->list_ptr); - array_free(&pos->users); - if (pos->op == IR_INSN_PHI) { - array_free(&pos->phi); - } - free(pos); - } - free(bb); - } - for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { - array_free(&fun->function_arg[i]->users); - __free(fun->function_arg[i]); - } - array_free(&fun->all_bbs); - array_free(&fun->reachable_bbs); - array_free(&fun->end_bbs); - array_free(&fun->cg_info.all_var); -} - -struct ir_function gen_function(struct ssa_transform_env *env) { - struct ir_function fun; - fun.arg_num = 1; - fun.entry = env->info.entry->ir_bb; - fun.sp_users = env->sp_users; - for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { - fun.function_arg[i] = env->function_arg[i]; - } - fun.all_bbs = INIT_ARRAY(struct ir_basic_block *); - fun.reachable_bbs = INIT_ARRAY(struct ir_basic_block *); - fun.end_bbs = INIT_ARRAY(struct ir_basic_block *); - fun.cg_info.all_var = INIT_ARRAY(struct ir_insn *); - for (size_t i = 0; i < MAX_BPF_REG; ++i) { - struct array *currentDef = &env->currentDef[i]; - array_free(currentDef); - } - for (size_t i = 0; i < env->info.all_bbs.num_elem; ++i) { - struct pre_ir_basic_block *bb = ((struct bb_entrance_info *)(env->info.all_bbs.data))[i].bb; - array_free(&bb->preds); - array_free(&bb->succs); - free(bb->pre_insns); - bb->ir_bb->user_data = NULL; - array_push(&fun.all_bbs, &bb->ir_bb); - free(bb); - } - return fun; -} - -__u8 ir_value_equal(struct ir_value a, struct ir_value b) { - if (a.type != b.type) { - return 0; - } - if (a.type == IR_VALUE_CONSTANT) { - return a.data.constant_d.data.s32_d == b.data.constant_d.data.s32_d; - } - if (a.type == IR_VALUE_INSN) { - return a.data.insn_d == b.data.insn_d; - } - if (a.type == IR_VALUE_STACK_PTR) { - return 1; - } - CRITICAL("Error"); -} - -void run_passes(struct ir_function *fun) { - for (size_t i = 0; i < sizeof(passes) / sizeof(passes[0]); ++i) { - clean_env_all(fun); - gen_reachable_bbs(fun); - passes[i](fun); - printf("--------------------\n"); - // Validate the IR - check_users(fun); - print_ir_prog(fun); - } +static int init_ir_bbs(struct ssa_transform_env *env) +{ + for (size_t i = 0; i < env->info.all_bbs.num_elem; ++i) { + struct pre_ir_basic_block *bb = + ((struct bb_entrance_info *)(env->info.all_bbs.data))[i] + .bb; + init_ir_bb(bb); + } + // Set the preds and succs + for (size_t i = 0; i < env->info.all_bbs.num_elem; ++i) { + struct pre_ir_basic_block *bb = + ((struct bb_entrance_info *)(env->info.all_bbs.data))[i] + .bb; + struct ir_basic_block *irbb = bb->ir_bb; + for (size_t j = 0; j < bb->preds.num_elem; ++j) { + struct pre_ir_basic_block *pred = + ((struct pre_ir_basic_block * + *)(bb->preds.data))[j]; + bpf_ir_array_push(&irbb->preds, &pred->ir_bb); + } + for (size_t j = 0; j < bb->succs.num_elem; ++j) { + struct pre_ir_basic_block *succ = + ((struct pre_ir_basic_block * + *)(bb->succs.data))[j]; + bpf_ir_array_push(&irbb->succs, &succ->ir_bb); + } + } + return 0; +} + +static struct ir_basic_block * +get_ir_bb_from_position(struct ssa_transform_env *env, size_t pos) +{ + // Iterate through all the BBs + for (size_t i = 0; i < env->info.all_bbs.num_elem; ++i) { + struct bb_entrance_info *info = &( + (struct bb_entrance_info *)(env->info.all_bbs.data))[i]; + if (info->entrance == pos) { + return info->bb->ir_bb; + } + } + CRITICAL("Error"); +} + +static struct ir_value get_src_value(struct ssa_transform_env *env, + struct pre_ir_basic_block *bb, + struct pre_ir_insn insn) +{ + __u8 code = insn.opcode; + if (BPF_SRC(code) == BPF_K) { + return (struct ir_value){ .type = IR_VALUE_CONSTANT, + .data.constant_d = insn.imm }; + } else if (BPF_SRC(code) == BPF_X) { + return read_variable(env, insn.src_reg, bb); + } else { + CRITICAL("Error"); + } +} + +static struct ir_insn * +create_alu_bin(struct ir_basic_block *bb, struct ir_value val1, + struct ir_value val2, enum ir_insn_type ty, + struct ssa_transform_env *env, enum ir_alu_type alu_ty) +{ + struct ir_insn *new_insn = create_insn_back(bb); + new_insn->op = ty; + new_insn->values[0] = val1; + new_insn->values[1] = val2; + new_insn->value_num = 2; + new_insn->alu = alu_ty; + add_user(env, new_insn, new_insn->values[0]); + add_user(env, new_insn, new_insn->values[1]); + return new_insn; +} + +static void alu_write(struct ssa_transform_env *env, enum ir_insn_type ty, + struct pre_ir_insn insn, struct pre_ir_basic_block *bb, + enum ir_alu_type alu_ty) +{ + struct ir_insn *new_insn = + create_alu_bin(bb->ir_bb, read_variable(env, insn.dst_reg, bb), + get_src_value(env, bb, insn), ty, env, alu_ty); + struct ir_value new_val; + new_val.type = IR_VALUE_INSN; + new_val.data.insn_d = new_insn; + write_variable(env, insn.dst_reg, bb, new_val); +} + +static void create_cond_jmp(struct ssa_transform_env *env, + struct pre_ir_basic_block *bb, + struct pre_ir_insn insn, enum ir_insn_type ty, + enum ir_alu_type alu_ty) +{ + struct ir_insn *new_insn = create_insn_back(bb->ir_bb); + new_insn->op = ty; + new_insn->values[0] = read_variable(env, insn.dst_reg, bb); + new_insn->values[1] = get_src_value(env, bb, insn); + new_insn->value_num = 2; + new_insn->alu = alu_ty; + add_user(env, new_insn, new_insn->values[0]); + add_user(env, new_insn, new_insn->values[1]); + size_t pos = insn.pos + insn.off + 1; + new_insn->bb1 = get_ir_bb_from_position(env, insn.pos + 1); + new_insn->bb2 = get_ir_bb_from_position(env, pos); + bpf_ir_array_push(&new_insn->bb1->users, &new_insn); + bpf_ir_array_push(&new_insn->bb2->users, &new_insn); +} + +static int transform_bb(struct ssa_transform_env *env, + struct pre_ir_basic_block *bb) +{ + PRINT_LOG("Transforming BB%zu\n", bb->id); + if (bb->sealed) { + return 0; + } + // Try sealing a BB + __u8 pred_all_filled = 1; + for (size_t i = 0; i < bb->preds.num_elem; ++i) { + struct pre_ir_basic_block *pred = + ((struct pre_ir_basic_block **)(bb->preds.data))[i]; + if (!pred->filled) { + // Not filled + pred_all_filled = 0; + break; + } + } + if (pred_all_filled) { + seal_block(env, bb); + } + if (bb->filled) { + // Already visited (filled) + return 0; + } + // Fill the BB + for (size_t i = 0; i < bb->len; ++i) { + struct pre_ir_insn insn = bb->pre_insns[i]; + __u8 code = insn.opcode; + if (BPF_CLASS(code) == BPF_ALU || + BPF_CLASS(code) == BPF_ALU64) { + // ALU class + enum ir_alu_type alu_ty = IR_ALU_UNKNOWN; + if (BPF_CLASS(code) == BPF_ALU) { + alu_ty = IR_ALU_32; + } else { + alu_ty = IR_ALU_64; + } + if (BPF_OP(code) == BPF_ADD) { + alu_write(env, IR_INSN_ADD, insn, bb, alu_ty); + } else if (BPF_OP(code) == BPF_SUB) { + alu_write(env, IR_INSN_SUB, insn, bb, alu_ty); + } else if (BPF_OP(code) == BPF_MUL) { + alu_write(env, IR_INSN_MUL, insn, bb, alu_ty); + } else if (BPF_OP(code) == BPF_MOV) { + // Do not create instructions + write_variable(env, insn.dst_reg, bb, + get_src_value(env, bb, insn)); + } else if (BPF_OP(code) == BPF_LSH) { + alu_write(env, IR_INSN_LSH, insn, bb, alu_ty); + } else if (BPF_OP(code) == BPF_MOD) { + // dst = (src != 0) ? (dst % src) : dst + alu_write(env, IR_INSN_MOD, insn, bb, alu_ty); + } + + else { + // TODO + CRITICAL("Error"); + } + + } else if (BPF_CLASS(code) == BPF_LD && + BPF_MODE(code) == BPF_IMM && + BPF_SIZE(code) == BPF_DW) { + // 64-bit immediate load + if (insn.src_reg == 0x0) { + // immediate value + struct ir_value imm_val; + imm_val.type = IR_VALUE_CONSTANT; + imm_val.data.constant_d = insn.imm64; + write_variable(env, insn.dst_reg, bb, imm_val); + } else { + CRITICAL("Not supported"); + } + } else if (BPF_CLASS(code) == BPF_LDX && + BPF_MODE(code) == BPF_MEMSX) { + // dst = *(signed size *) (src + offset) + // https://www.kernel.org/doc/html/v6.6/bpf/standardization/instruction-set.html#sign-extension-load-operations + + struct ir_insn *new_insn = create_insn_back(bb->ir_bb); + new_insn->op = IR_INSN_LOADRAW; + struct ir_address_value addr_val; + addr_val.value = read_variable(env, insn.src_reg, bb); + add_user(env, new_insn, addr_val.value); + addr_val.offset = insn.off; + new_insn->vr_type = to_ir_ld_u(BPF_SIZE(code)); + new_insn->addr_val = addr_val; + + struct ir_value new_val; + new_val.type = IR_VALUE_INSN; + new_val.data.insn_d = new_insn; + write_variable(env, insn.dst_reg, bb, new_val); + } else if (BPF_CLASS(code) == BPF_LDX && + BPF_MODE(code) == BPF_MEM) { + // Regular load + // dst = *(unsigned size *) (src + offset) + // https://www.kernel.org/doc/html/v6.6/bpf/standardization/instruction-set.html#regular-load-and-store-operations + // TODO: use LOAD instead of LOADRAW + struct ir_insn *new_insn = create_insn_back(bb->ir_bb); + new_insn->op = IR_INSN_LOADRAW; + struct ir_address_value addr_val; + addr_val.value = read_variable(env, insn.src_reg, bb); + add_user(env, new_insn, addr_val.value); + addr_val.offset = insn.off; + new_insn->vr_type = to_ir_ld_u(BPF_SIZE(code)); + new_insn->addr_val = addr_val; + + struct ir_value new_val; + new_val.type = IR_VALUE_INSN; + new_val.data.insn_d = new_insn; + write_variable(env, insn.dst_reg, bb, new_val); + } else if (BPF_CLASS(code) == BPF_ST && + BPF_MODE(code) == BPF_MEM) { + // *(size *) (dst + offset) = imm32 + struct ir_insn *new_insn = create_insn_back(bb->ir_bb); + new_insn->op = IR_INSN_STORERAW; + struct ir_address_value addr_val; + addr_val.value = read_variable(env, insn.dst_reg, bb); + add_user(env, new_insn, addr_val.value); + addr_val.offset = insn.off; + new_insn->vr_type = to_ir_ld_u(BPF_SIZE(code)); + new_insn->addr_val = addr_val; + new_insn->values[0].type = IR_VALUE_CONSTANT; + new_insn->values[0].data.constant_d = insn.imm; + new_insn->value_num = 1; + } else if (BPF_CLASS(code) == BPF_STX && + BPF_MODE(code) == BPF_MEM) { + // *(size *) (dst + offset) = src + struct ir_insn *new_insn = create_insn_back(bb->ir_bb); + new_insn->op = IR_INSN_STORERAW; + struct ir_address_value addr_val; + addr_val.value = read_variable(env, insn.dst_reg, bb); + add_user(env, new_insn, addr_val.value); + addr_val.offset = insn.off; + new_insn->vr_type = to_ir_ld_u(BPF_SIZE(code)); + new_insn->addr_val = addr_val; + new_insn->values[0] = + read_variable(env, insn.src_reg, bb); + new_insn->value_num = 1; + add_user(env, new_insn, new_insn->values[0]); + } else if (BPF_CLASS(code) == BPF_JMP || + BPF_CLASS(code) == BPF_JMP32) { + enum ir_alu_type alu_ty = IR_ALU_UNKNOWN; + if (BPF_CLASS(code) == BPF_JMP) { + alu_ty = IR_ALU_64; + } else { + alu_ty = IR_ALU_32; + } + if (BPF_OP(code) == BPF_JA) { + // Direct Jump + // PC += offset + struct ir_insn *new_insn = + create_insn_back(bb->ir_bb); + new_insn->op = IR_INSN_JA; + size_t pos = insn.pos + insn.off + 1; + new_insn->bb1 = + get_ir_bb_from_position(env, pos); + bpf_ir_array_push(&new_insn->bb1->users, + &new_insn); + } else if (BPF_OP(code) == BPF_EXIT) { + // Exit + struct ir_insn *new_insn = + create_insn_back(bb->ir_bb); + new_insn->op = IR_INSN_RET; + new_insn->values[0] = + read_variable(env, BPF_REG_0, bb); + new_insn->value_num = 1; + } else if (BPF_OP(code) == BPF_JEQ) { + // PC += offset if dst == src + create_cond_jmp(env, bb, insn, IR_INSN_JEQ, + alu_ty); + } else if (BPF_OP(code) == BPF_JLT) { + // PC += offset if dst < src + create_cond_jmp(env, bb, insn, IR_INSN_JLT, + alu_ty); + } else if (BPF_OP(code) == BPF_JLE) { + // PC += offset if dst <= src + create_cond_jmp(env, bb, insn, IR_INSN_JLE, + alu_ty); + } else if (BPF_OP(code) == BPF_JGT) { + // PC += offset if dst > src + create_cond_jmp(env, bb, insn, IR_INSN_JGT, + alu_ty); + } else if (BPF_OP(code) == BPF_JGE) { + // PC += offset if dst >= src + create_cond_jmp(env, bb, insn, IR_INSN_JGE, + alu_ty); + } else if (BPF_OP(code) == BPF_JNE) { + // PC += offset if dst != src + create_cond_jmp(env, bb, insn, IR_INSN_JNE, + alu_ty); + } else if (BPF_OP(code) == BPF_CALL) { + // imm is the function id + struct ir_insn *new_insn = + create_insn_back(bb->ir_bb); + new_insn->op = IR_INSN_CALL; + new_insn->fid = insn.imm; + if (insn.imm < 0) { + PRINT_LOG( + "Not supported function call\n"); + new_insn->value_num = 0; + } else { + new_insn->value_num = + helper_func_arg_num[insn.imm]; + if (new_insn->value_num > + MAX_FUNC_ARG) { + CRITICAL("Too many arguments"); + } + for (size_t j = 0; + j < new_insn->value_num; ++j) { + new_insn->values[j] = + read_variable( + env, + BPF_REG_1 + j, + bb); + add_user(env, new_insn, + new_insn->values[j]); + } + } + + struct ir_value new_val; + new_val.type = IR_VALUE_INSN; + new_val.data.insn_d = new_insn; + write_variable(env, BPF_REG_0, bb, new_val); + } else { + // TODO + CRITICAL("Error"); + } + } else { + // TODO + PRINT_LOG("Class 0x%02x not supported\n", + BPF_CLASS(code)); + CRITICAL("Error"); + } + } + bb->filled = 1; + // Finish filling + for (size_t i = 0; i < bb->succs.num_elem; ++i) { + struct pre_ir_basic_block *succ = + ((struct pre_ir_basic_block **)(bb->succs.data))[i]; + transform_bb(env, succ); + } + return 0; +} + +static void free_function(struct ir_function *fun) +{ + bpf_ir_array_free(&fun->sp_users); + for (size_t i = 0; i < fun->all_bbs.num_elem; ++i) { + struct ir_basic_block *bb = + ((struct ir_basic_block **)(fun->all_bbs.data))[i]; + + bpf_ir_array_free(&bb->preds); + bpf_ir_array_free(&bb->succs); + bpf_ir_array_free(&bb->users); + // Free the instructions + struct ir_insn *pos = NULL, *n = NULL; + list_for_each_entry_safe(pos, n, &bb->ir_insn_head, list_ptr) { + list_del(&pos->list_ptr); + bpf_ir_array_free(&pos->users); + if (pos->op == IR_INSN_PHI) { + bpf_ir_array_free(&pos->phi); + } + free_proto(pos); + } + free_proto(bb); + } + for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { + bpf_ir_array_free(&fun->function_arg[i]->users); + free_proto(fun->function_arg[i]); + } + bpf_ir_array_free(&fun->all_bbs); + bpf_ir_array_free(&fun->reachable_bbs); + bpf_ir_array_free(&fun->end_bbs); + bpf_ir_array_free(&fun->cg_info.all_var); + if (fun->cg_info.prog) { + free_proto(fun->cg_info.prog); + } +} + +static int gen_function(struct ir_function *fun, struct ssa_transform_env *env) +{ + fun->arg_num = 1; + fun->entry = env->info.entry->ir_bb; + fun->sp_users = env->sp_users; + for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { + fun->function_arg[i] = env->function_arg[i]; + } + INIT_ARRAY(&fun->all_bbs, struct ir_basic_block *); + INIT_ARRAY(&fun->reachable_bbs, struct ir_basic_block *); + INIT_ARRAY(&fun->end_bbs, struct ir_basic_block *); + INIT_ARRAY(&fun->cg_info.all_var, struct ir_insn *); + fun->cg_info.prog = NULL; + fun->cg_info.prog_size = 0; + for (size_t i = 0; i < MAX_BPF_REG; ++i) { + struct array *currentDef = &env->currentDef[i]; + bpf_ir_array_free(currentDef); + } + for (size_t i = 0; i < env->info.all_bbs.num_elem; ++i) { + struct pre_ir_basic_block *bb = + ((struct bb_entrance_info *)(env->info.all_bbs.data))[i] + .bb; + bpf_ir_array_free(&bb->preds); + bpf_ir_array_free(&bb->succs); + free_proto(bb->pre_insns); + bb->ir_bb->user_data = NULL; + bpf_ir_array_push(&fun->all_bbs, &bb->ir_bb); + free_proto(bb); + } + return 0; +} + +static void bpf_ir_fix_bb_succ(struct ir_function *fun) +{ + struct ir_basic_block **pos; + array_for(pos, fun->all_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn = bpf_ir_get_last_insn(bb); + if (insn && is_cond_jmp(insn)) { + // Conditional jmp + if (bb->succs.num_elem != 2) { + CRITICAL( + "Conditional jmp with != 2 successors"); + } + struct ir_basic_block **s1 = array_get( + &bb->succs, 0, struct ir_basic_block *); + struct ir_basic_block **s2 = array_get( + &bb->succs, 1, struct ir_basic_block *); + *s1 = insn->bb1; + *s2 = insn->bb2; + } + } +} + +static void add_reach(struct ir_function *fun, struct ir_basic_block *bb) +{ + if (bb->_visited) { + return; + } + bb->_visited = 1; + bpf_ir_array_push(&fun->reachable_bbs, &bb); + + struct ir_basic_block **succ; + __u8 i = 0; + array_for(succ, bb->succs) + { + if (i == 0) { + i = 1; + // Check if visited + if ((*succ)->_visited) { + CRITICAL("Loop BB detected"); + } + } + add_reach(fun, *succ); + } +} + +static void gen_reachable_bbs(struct ir_function *fun) +{ + clean_env(fun); + bpf_ir_array_clear(&fun->reachable_bbs); + add_reach(fun, fun->entry); +} + +static void gen_end_bbs(struct ir_function *fun) +{ + struct ir_basic_block **pos; + bpf_ir_array_clear(&fun->end_bbs); + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + if (bb->succs.num_elem == 0) { + bpf_ir_array_push(&fun->end_bbs, &bb); + } + } +} + +static int run_passes(struct ir_function *fun) +{ + bpf_ir_prog_check(fun); + for (size_t i = 0; i < sizeof(passes) / sizeof(passes[0]); ++i) { + bpf_ir_fix_bb_succ(fun); + clean_env_all(fun); + gen_reachable_bbs(fun); + gen_end_bbs(fun); + PRINT_LOG("\x1B[32m------ Running Pass: %s ------\x1B[0m\n", + passes[i].name); + passes[i].pass(fun); + // Validate the IR + bpf_ir_prog_check(fun); + print_ir_prog(fun); + } + bpf_ir_fix_bb_succ(fun); + clean_env_all(fun); + gen_reachable_bbs(fun); + gen_end_bbs(fun); + return 0; +} + +static void print_bpf_insn(struct bpf_insn insn) +{ + if (insn.off < 0) { + PRINT_LOG("%4x %x %x %8x -%8x\n", insn.code, + insn.src_reg, insn.dst_reg, insn.imm, -insn.off); + } else { + PRINT_LOG("%4x %x %x %8x %8x\n", insn.code, + insn.src_reg, insn.dst_reg, insn.imm, insn.off); + } +} + +static void print_bpf_prog(struct bpf_insn *insns, size_t len) +{ + PRINT_LOG("code src_reg dst_reg imm off\n"); + for (size_t i = 0; i < len; ++i) { + struct bpf_insn insn = insns[i]; + print_bpf_insn(insn); + } } // Interface implementation -void run(struct bpf_insn *insns, size_t len) { - struct bb_info info = gen_bb(insns, len); - print_pre_ir_cfg(info.entry); - struct ssa_transform_env env = init_env(info); - init_ir_bbs(&env); - transform_bb(&env, info.entry); - struct ir_function fun = gen_function(&env); - fix_bb_succ(&fun); - // Drop env - print_ir_prog(&fun); - printf("--------------------\n"); - // Start IR manipulation - - run_passes(&fun); - - // End IR manipulation - // printf("--------------------\n"); - // print_ir_prog(&fun); - - // Test - // add_stack_offset(&fun, -8); - // printf("--------------------\n"); - // print_ir_prog(&fun); - - printf("--------------------\n"); - code_gen(&fun); - - // Free the memory - free_function(&fun); +int bpf_ir_run(struct bpf_insn *insns, size_t len) +{ + struct bb_info info; + int ret = 0; + ret = gen_bb(&info, insns, len); + if (ret) { + return ret; + } + + print_pre_ir_cfg(info.entry); + struct ssa_transform_env env; + ret = init_env(&env, info); + if (ret) { + return ret; + } + ret = init_ir_bbs(&env); + if (ret) { + return ret; + } + ret = transform_bb(&env, info.entry); + if (ret) { + return ret; + } + struct ir_function fun; + ret = gen_function(&fun, &env); + if (ret) { + return ret; + } + + // Drop env + print_ir_prog(&fun); + PRINT_LOG("Starting IR Passes...\n"); + // Start IR manipulation + + ret = run_passes(&fun); + if (ret) { + return ret; + } + + // End IR manipulation + PRINT_LOG("IR Passes Ended!\n"); + + ret = bpf_ir_code_gen(&fun); + if (ret) { + return ret; + } + + // Got the bpf bytecode + + PRINT_LOG("--------------------\nOriginal Program:\n"); + print_bpf_prog(insns, len); + PRINT_LOG("--------------------\nRewritten Program %zu:\n", + fun.cg_info.prog_size); + print_bpf_prog(fun.cg_info.prog, fun.cg_info.prog_size); + + // Free the memory + free_function(&fun); + return 0; } diff --git a/docs/IR.md b/IR/docs/IR.md similarity index 100% rename from docs/IR.md rename to IR/docs/IR.md diff --git a/IR/docs/Type.md b/IR/docs/Type.md new file mode 100644 index 00000000..e51b7ed9 --- /dev/null +++ b/IR/docs/Type.md @@ -0,0 +1,38 @@ +# IR Type System + +Design goal: we want the type system be as minimal as possible BUT enough to generate correct code. + +It's IMPOSSIBLE to generate correct code without type information. + +Here "type" information is about the size of a data which assembly language cares about, not about any other real types in C. + +Correctness: + +- The behavior of original program doesn't change. + +We do not change the OP type of ALU instructions in the original program. + +All ALU instructions have 2 mode: 32 bits/64 bits. + +``` +a = add x y +b = add64 x y +``` + +It's difficult to say what is correct, we could let users specify the type when doing ALU opetations. + +## Constant Type + +There is only one type: `64`. + +All other size could be encoded into this `64` data. + +## Load/Store/Alloc + +`Store` & `Load` use the size defined in alloc. + +## Loadraw/Storeraw + +`Loadraw` provides the address and size directly. + +`Storeraw` provides the size and address, along with a value. diff --git a/IR/include/array.h b/IR/include/array.h deleted file mode 100644 index 2c7ee86a..00000000 --- a/IR/include/array.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef __ARRAY_H__ -#define __ARRAY_H__ - -#include -#include - -struct array { - void *data; - size_t num_elem; // Current length - size_t max_elem; // Maximum length - size_t elem_size; -}; - -struct array array_init(size_t); -void array_push(struct array *, void *); -void array_push_unique(struct array *arr, void *data); -void array_free(struct array *); -struct array array_null(); -void array_erase(struct array *arr, size_t idx); -void *array_get_void(struct array *arr, size_t idx); -#define array_get(arr, idx, type) ((type *)array_get_void(arr, idx)) -void *__malloc(size_t size); -void __free(void *ptr); -void array_clear(struct array *arr); -struct array array_clone(struct array *arr); - -#define array_for(pos, arr) \ - for (pos = ((typeof(pos))(arr.data)); pos < (typeof(pos))(arr.data) + arr.num_elem; pos++) - -#define INIT_ARRAY(type) array_init(sizeof(type)) - -#endif diff --git a/IR/include/bpf_ir.h b/IR/include/bpf_ir.h deleted file mode 100644 index f9dd4cf2..00000000 --- a/IR/include/bpf_ir.h +++ /dev/null @@ -1,332 +0,0 @@ -#ifndef __BPF_IR_H__ -#define __BPF_IR_H__ - -#include -#include -#include "array.h" -#include "list.h" - -#define MAX_FUNC_ARG 5 - -enum imm_type { - IMM, - IMM64 -}; - -/** - Pre-IR instructions, similar to `bpf_insn` - */ -struct pre_ir_insn { - __u8 opcode; - - __u8 dst_reg; - __u8 src_reg; - __s16 off; - - enum imm_type it; - __s32 imm; - __s64 imm64; // Immediate constant for 64-bit immediate - - size_t pos; // Original position -}; - -enum ir_constant_type { - IR_CONSTANT_U16, - IR_CONSTANT_S16, - IR_CONSTANT_U32, - IR_CONSTANT_S32, - IR_CONSTANT_U64, - IR_CONSTANT_S64, -}; - -/** - IR Constants - */ -struct ir_constant { - union { - __u16 u16_d; - __s16 s16_d; - __u32 u32_d; - __s32 s32_d; - __u64 u64_d; - __s64 s64_d; - } data; - enum ir_constant_type type; -}; - -enum ir_value_type { - IR_VALUE_CONSTANT, - IR_VALUE_INSN, - IR_VALUE_STACK_PTR, - IR_VALUE_UNDEF, -}; - -/** - VALUE = CONSTANT | INSN - - "r1 = constant" pattern will use `CONSTANT` which will not be added to BB. - */ -struct ir_value { - union { - struct ir_constant constant_d; - struct ir_insn *insn_d; - } data; - enum ir_value_type type; -}; - -struct ir_value ir_value_insn(struct ir_insn *); - -struct ir_value ir_value_stack_ptr(); - -/** - Value plus an offset - */ -struct ir_address_value { - // The value might be stack pointer - struct ir_value value; - __s16 offset; -}; - -/** - A single phi value entry - */ -struct phi_value { - struct ir_value value; - struct ir_basic_block *bb; -}; - -/** - Virtual Register Type - */ -enum ir_vr_type { - IR_VR_TYPE_8, - IR_VR_TYPE_16, - IR_VR_TYPE_32, - IR_VR_TYPE_64, -}; - -enum ir_insn_type { - IR_INSN_ALLOC, - IR_INSN_STORE, - IR_INSN_LOAD, - IR_INSN_STORERAW, - IR_INSN_LOADRAW, - IR_INSN_FUNCTIONARG, // The function argument store, not an actual instruction - // ALU - IR_INSN_ADD, - IR_INSN_SUB, - IR_INSN_MUL, - IR_INSN_LSH, - IR_INSN_MOD, - // CALL EXIT - IR_INSN_CALL, - IR_INSN_RET, - // JMP - IR_INSN_JA, - IR_INSN_JEQ, - IR_INSN_JGT, - IR_INSN_JGE, - IR_INSN_JLT, - IR_INSN_JLE, - IR_INSN_JNE, - // PHI - IR_INSN_PHI, - // Code-gen instructions - IR_INSN_ASSIGN, - IR_INSN_REG, -}; - -/** - INSN = - ALLOC - | STORE , - | LOAD - | STORERAW , - | LOADRAW - | FUNCTIONARG - - | ADD , - | SUB , - | MUL , - | LSH , - | MOD , - | CALL - | RET - | JA - | JEQ , , , - | JGT , , , - | JGE , , , - | JLT , , , - | JLE , , , - | JNE , , , - | PHI - (For code gen usage) - | ASSIGN - | REG - - Note. must be the next basic block. - ASSIGN dst cannot be callee-saved registers - */ -struct ir_insn { - struct ir_value values[MAX_FUNC_ARG]; - __u8 value_num; - - // Used in ALLOC and instructions - enum ir_vr_type vr_type; - - // Used in RAW instructions - struct ir_address_value addr_val; - - // Used in JMP instructions - struct ir_basic_block *bb1; - struct ir_basic_block *bb2; - - // Array of phi_value - struct array phi; - - __s32 fid; - enum ir_insn_type op; - - // Linked list - struct list_head list_ptr; - - // Parent BB - struct ir_basic_block *parent_bb; - - // Array of struct ir_insn * - // Users - struct array users; - - // Used when generating the real code - size_t _insn_id; - void *user_data; - __u8 _visited; -}; - -/** - Pre-IR BB - - This includes many data structures needed to generate the IR. - */ -struct pre_ir_basic_block { - // An ID used to debug - size_t id; - - // Start position in the original insns - size_t start_pos; - - // End position in the original insns - size_t end_pos; - - // The number of instructions in this basic block (modified length) - size_t len; - - struct pre_ir_insn *pre_insns; - - struct array preds; - struct array succs; - - __u8 visited; - - __u8 sealed; - __u8 filled; - struct ir_basic_block *ir_bb; - struct ir_insn *incompletePhis[MAX_BPF_REG]; -}; - -/** - IR Basic Block - */ -struct ir_basic_block { - struct list_head ir_insn_head; - - // Array of struct ir_basic_block * - struct array preds; - - // Array of struct ir_basic_block * - struct array succs; - - // Used for construction and debugging - __u8 _visited; - size_t _id; - void *user_data; - - // Array of struct ir_insn * - struct array users; -}; - -/** - The BB value used in currentDef - */ -struct bb_val { - struct pre_ir_basic_block *bb; - struct ir_value val; -}; - -/** - BB with the raw entrance position - */ -struct bb_entrance_info { - size_t entrance; - struct pre_ir_basic_block *bb; -}; - -/** - Generated BB information - */ -struct bb_info { - struct pre_ir_basic_block *entry; - - // Array of bb_entrance_info - struct array all_bbs; -}; - -/** - The environment data for transformation - */ -struct ssa_transform_env { - // Array of bb_val (which is (BB, Value) pair) - struct array currentDef[MAX_BPF_REG]; - struct bb_info info; - - // Stack pointer (r10) users - struct array sp_users; - // Function argument - struct ir_insn *function_arg[MAX_FUNC_ARG]; -}; - -struct error { - __u8 is_kernel_err : 1; - unsigned int errorno : 31; -}; - -// helper functions - -void write_variable(struct ssa_transform_env *env, __u8 reg, struct pre_ir_basic_block *bb, - struct ir_value val); - -struct ir_value read_variable_recursive(struct ssa_transform_env *env, __u8 reg, - struct pre_ir_basic_block *bb); - -struct ir_value read_variable(struct ssa_transform_env *env, __u8 reg, - struct pre_ir_basic_block *bb); - -void construct_ir(struct bpf_insn *insns, size_t len); - -struct bb_info gen_bb(struct bpf_insn *insns, size_t len); - -struct ir_insn *add_phi_operands(struct ssa_transform_env *env, __u8 reg, struct ir_insn *insn); - -struct ir_insn *create_insn_back(struct ir_basic_block *bb); - -struct ir_insn *create_insn_front(struct ir_basic_block *bb); - -void add_user(struct ssa_transform_env *env, struct ir_insn *user, struct ir_value val); - -__u8 ir_value_equal(struct ir_value a, struct ir_value b); - -struct ir_basic_block *init_ir_bb_raw(); - -int vr_type_to_size(enum ir_vr_type type); - -#endif diff --git a/IR/include/code_gen.h b/IR/include/code_gen.h deleted file mode 100644 index 4ad2e1aa..00000000 --- a/IR/include/code_gen.h +++ /dev/null @@ -1,104 +0,0 @@ -#ifndef __BPF_IR_CODE_GEN_H__ -#define __BPF_IR_CODE_GEN_H__ - -#include -#include "bpf_ir.h" -#include "ir_fun.h" - -void code_gen(struct ir_function *fun); - -// Extra information needed for code gen -struct ir_bb_cg_extra { - size_t insn_cnt; -}; - -struct ir_insn_cg_extra { - // Destination (Not in SSA form anymore) - struct ir_insn *dst; - - // Liveness analysis - struct array in; - struct array out; - struct array gen; - struct array kill; - - // Adj list in interference graph - // Array of struct ir_insn* - struct array adj; - - // Translated pre_ir_insn - struct pre_ir_insn translated[2]; - - // Translated number - __u8 translated_num; - - // Whether the VR is allocated with a real register - // If it's a pre-colored register, it's also 1 - __u8 allocated; - - // When allocating register, whether dst will be spilled - // 0: Not spilled - // 1: Spilled on stack position 1 - // etc. - size_t spilled; - - // Valid if spilled == 0 && allocated == 1 - // Valid number: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 - __u8 alloc_reg; -}; - -enum val_type { - UNDEF, - REG, - CONST, - STACK, -}; - -struct ir_insn_cg_extra *insn_cg(struct ir_insn *insn); - -struct ir_insn_cg_extra *init_insn_cg(struct ir_insn *insn); - -struct ir_insn *dst(struct ir_insn *insn); - -void to_cssa(struct ir_function *fun); - -void remove_phi(struct ir_function *fun); - -void print_ir_prog_cg(struct ir_function *fun); - -void liveness_analysis(struct ir_function *fun); - -void conflict_analysis(struct ir_function *fun); - -void print_interference_graph(struct ir_function *fun); - -void graph_coloring(struct ir_function *fun); - -void explicit_reg(struct ir_function *fun); - -void coaleasing(struct ir_function *fun); - -enum val_type vtype(struct ir_value val); - -int check_need_spill(struct ir_function *fun); - -void translate(struct ir_function *fun); - -void spill_callee(struct ir_function *fun); - -enum val_type vtype_insn(struct ir_insn *insn); - -enum val_type vtype(struct ir_value val); - -void calc_callee_num(struct ir_function *fun); - -void calc_stack_size(struct ir_function *fun); - -// Add stack offset to all stack access -void add_stack_offset(struct ir_function *fun, __s16 offset); - -void normalize(struct ir_function *fun); - -void relocate(struct ir_function *fun); - -#endif diff --git a/IR/include/constraint.h b/IR/include/constraint.h deleted file mode 100644 index aa95ee4e..00000000 --- a/IR/include/constraint.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef __BPF_IR_CONSTRAINT_H__ -#define __BPF_IR_CONSTRAINT_H__ - -#include "bpf_ir.h" - -enum constraint_type { - CONSTRAINT_TYPE_VALUE_EQUAL, - CONSTRAINT_TYPE_VALUE_RANGE -}; - -struct ir_constraint { - enum constraint_type type; - - // Range: [start, end) - struct ir_value start; - struct ir_value end; - - // Constrain value - struct ir_value cval; - - // Real value to be compared - struct ir_value val; - struct ir_insn *pos; -}; - -#endif diff --git a/IR/include/dbg.h b/IR/include/dbg.h deleted file mode 100644 index c726bfc9..00000000 --- a/IR/include/dbg.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef DEBUG_H -#define DEBUG_H - -#include -#include -#include - -#define CRITICAL(str) \ - { \ - printf("<%s>:%d %s\n", __FUNCTION__, __LINE__, str); \ - exit(1); \ - } - -#define DBGASSERT(cond) \ - if (!(cond)) { \ - CRITICAL("Assertion failed"); \ - } - -#endif diff --git a/IR/include/ir_bb.h b/IR/include/ir_bb.h deleted file mode 100644 index e6cf6bc1..00000000 --- a/IR/include/ir_bb.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef __BPF_IR_BB_H__ -#define __BPF_IR_BB_H__ - -#include "bpf_ir.h" -#include "ir_fun.h" - -/// Get the number of instructions in a basic block -size_t bb_len(struct ir_basic_block *); - -struct ir_basic_block *create_bb(struct ir_function *fun); - -void connect_bb(struct ir_basic_block *from, struct ir_basic_block *to); - -void disconnect_bb(struct ir_basic_block *from, struct ir_basic_block *to); - -/// Split a BB after an instruction -struct ir_basic_block *split_bb(struct ir_function *fun, struct ir_insn *insn); - -struct ir_insn *get_last_insn(struct ir_basic_block *bb); - -struct ir_insn *get_first_insn(struct ir_basic_block *bb); - -int bb_empty(struct ir_basic_block *bb); - -#endif diff --git a/IR/include/ir_fun.h b/IR/include/ir_fun.h deleted file mode 100644 index 825c263d..00000000 --- a/IR/include/ir_fun.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef __BPF_IR_FUN_H__ -#define __BPF_IR_FUN_H__ - -#include "bpf_ir.h" - -struct code_gen_info { - // All vertex in interference graph - // Array of struct ir_insn* - struct array all_var; - - // BPF Register Virtual Instruction (used as dst) - struct ir_insn *regs[MAX_BPF_REG]; - - size_t callee_num; - - __s16 stack_offset; -}; - -struct ir_function { - size_t arg_num; - - // Array of struct ir_basic_block * - struct array all_bbs; - - // The entry block - struct ir_basic_block *entry; - - // Store any information about the function - struct array reachable_bbs; - - // BBs who has no successors - struct array end_bbs; - - // Stack pointer (r10) users. Should be readonly. No more manual stack access should be allowed. - struct array sp_users; - - // Function argument - struct ir_insn *function_arg[MAX_FUNC_ARG]; - - // Array of struct ir_constraint. Value constraints. - struct array value_constraints; - - struct code_gen_info cg_info; -}; - -// Constructor and Destructor - -struct ir_function gen_function(struct ssa_transform_env *env); - -void free_function(struct ir_function *fun); - -void fix_bb_succ(struct ir_function *fun); - -#endif diff --git a/IR/include/ir_helper.h b/IR/include/ir_helper.h deleted file mode 100644 index 96f8450c..00000000 --- a/IR/include/ir_helper.h +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef __BPF_IR_HELPER_H__ -#define __BPF_IR_HELPER_H__ - -#include "ir_fun.h" - -void clean_env_all(struct ir_function *fun); - -void print_ir_prog(struct ir_function *); - -void print_ir_prog_advanced(struct ir_function *, void (*)(struct ir_basic_block *), - void (*)(struct ir_insn *), void (*)(struct ir_insn *)); - -void print_ir_dst(struct ir_insn *insn); - -void print_ir_alloc(struct ir_insn *insn); - -void clean_env(struct ir_function *); - -void clean_env_all(struct ir_function *fun); - -// Tag the instruction and BB -void tag_ir(struct ir_function *fun); - -// Remove all tag information -void clean_tag(struct ir_function *); - -void print_constant(struct ir_constant d); - -void print_address_value(struct ir_address_value v); - -void print_vr_type(enum ir_vr_type t); - -void print_phi(struct array *phi); - -void assign_id(struct ir_basic_block *bb, size_t *cnt, size_t *bb_cnt); - -void print_ir_insn(struct ir_insn *); - -void print_ir_value(struct ir_value v); - -void print_raw_ir_insn(struct ir_insn *insn); - -void print_raw_ir_bb(struct ir_basic_block *bb); - -void print_insn_ptr_base(struct ir_insn *insn); - -#endif diff --git a/IR/include/ir_insn.h b/IR/include/ir_insn.h deleted file mode 100644 index eccdac74..00000000 --- a/IR/include/ir_insn.h +++ /dev/null @@ -1,114 +0,0 @@ -#ifndef __BPF_IR_INSN_H__ -#define __BPF_IR_INSN_H__ - -#include "bpf_ir.h" -#include "list.h" - -enum insert_position { - INSERT_BACK, - INSERT_FRONT, - // BB-specific - INSERT_BACK_BEFORE_JMP, - INSERT_FRONT_AFTER_PHI -}; - -// Return an array of struct ir_value* -struct array get_operands(struct ir_insn *insn); - -void replace_all_usage(struct ir_insn *insn, struct ir_value rep); - -void replace_all_usage_except(struct ir_insn *insn, struct ir_value rep, struct ir_insn *except); - -void erase_insn(struct ir_insn *insn); - -// Erase an instruction without checking the users -// Used in code gen -void erase_insn_raw(struct ir_insn *insn); - -int is_void(struct ir_insn *insn); - -int is_jmp(struct ir_insn *insn); - -struct ir_insn *prev_insn(struct ir_insn *insn); - -struct ir_insn *next_insn(struct ir_insn *insn); - -struct ir_insn *create_alloc_insn(struct ir_insn *insn, enum ir_vr_type type, - enum insert_position pos); - -struct ir_insn *create_alloc_insn_bb(struct ir_basic_block *bb, enum ir_vr_type type, - enum insert_position pos); - -struct ir_insn *create_store_insn(struct ir_insn *insn, struct ir_insn *st_insn, - struct ir_value val, enum insert_position pos); - -struct ir_insn *create_store_insn_bb(struct ir_basic_block *bb, struct ir_insn *st_insn, - struct ir_value val, enum insert_position pos); - -struct ir_insn *create_load_insn(struct ir_insn *insn, enum ir_vr_type ty, struct ir_value val, - enum insert_position pos); - -struct ir_insn *create_load_insn_bb(struct ir_basic_block *bb, enum ir_vr_type ty, - struct ir_value val, enum insert_position pos); - -struct ir_insn *create_bin_insn(struct ir_insn *insn, struct ir_value val1, struct ir_value val2, - enum ir_insn_type ty, enum insert_position pos); - -struct ir_insn *create_bin_insn_bb(struct ir_basic_block *bb, struct ir_value val1, - struct ir_value val2, enum ir_insn_type ty, - enum insert_position pos); - -struct ir_insn *create_ja_insn(struct ir_insn *insn, struct ir_basic_block *to_bb, - enum insert_position pos); - -struct ir_insn *create_ja_insn_bb(struct ir_basic_block *bb, struct ir_basic_block *to_bb, - enum insert_position pos); - -struct ir_insn *create_jbin_insn(struct ir_insn *insn, struct ir_value val1, struct ir_value val2, - struct ir_basic_block *to_bb1, struct ir_basic_block *to_bb2, - enum ir_insn_type ty, enum insert_position pos); - -struct ir_insn *create_jbin_insn_bb(struct ir_basic_block *bb, struct ir_value val1, - struct ir_value val2, struct ir_basic_block *to_bb1, - struct ir_basic_block *to_bb2, enum ir_insn_type ty, - enum insert_position pos); - -struct ir_insn *create_ret_insn(struct ir_insn *insn, struct ir_value val, - enum insert_position pos); - -struct ir_insn *create_ret_insn_bb(struct ir_basic_block *bb, struct ir_value val, - enum insert_position pos); - -struct ir_insn *create_assign_insn(struct ir_insn *insn, struct ir_value val, - enum insert_position pos); - -struct ir_insn *create_assign_insn_bb(struct ir_basic_block *bb, struct ir_value val, - enum insert_position pos); - -struct ir_insn *create_phi_insn(struct ir_insn *insn, enum insert_position pos); - -struct ir_insn *create_phi_insn_bb(struct ir_basic_block *bb, enum insert_position pos); - -void phi_add_operand(struct ir_insn *insn, struct ir_basic_block *bb, struct ir_value val); - -void val_add_user(struct ir_value val, struct ir_insn *user); - -void val_remove_user(struct ir_value val, struct ir_insn *user); - -struct ir_insn *create_assign_insn_cg(struct ir_insn *insn, struct ir_value val, - enum insert_position pos); - -struct ir_insn *create_assign_insn_bb_cg(struct ir_basic_block *bb, struct ir_value val, - enum insert_position pos); - -void replace_operand(struct ir_insn *insn, struct ir_value v1, struct ir_value v2); - -struct ir_insn *create_insn_base_cg(struct ir_basic_block *bb); - -struct ir_insn *create_insn_base(struct ir_basic_block *bb); - -void insert_at(struct ir_insn *new_insn, struct ir_insn *insn, enum insert_position pos); - -void insert_at_bb(struct ir_insn *new_insn, struct ir_basic_block *bb, enum insert_position pos); - -#endif diff --git a/IR/include/linux/bpf_ir.h b/IR/include/linux/bpf_ir.h new file mode 100644 index 00000000..827533ae --- /dev/null +++ b/IR/include/linux/bpf_ir.h @@ -0,0 +1,793 @@ +#ifndef _LINUX_BPF_IR_H +#define _LINUX_BPF_IR_H + +#include + +#ifndef __KERNEL__ +#include +#include +#include +#include +#include "list.h" +#include + +#define PRINT_LOG printf + +#include "stdint.h" + +#define SIZET_MAX SIZE_MAX + +#else + +#include +#include +#include + +#define PRINT_LOG printk + +#define SIZET_MAX ULONG_MAX + +#define qsort(a, b, c, d) sort(a, b, c, d, NULL) + +#endif + +/* Array Start */ + +struct array { + void *data; + size_t num_elem; // Current length + size_t max_elem; // Maximum length + size_t elem_size; +}; + +void bpf_ir_array_init(struct array *res, size_t size); + +int bpf_ir_array_push(struct array *, void *); + +int bpf_ir_array_push_unique(struct array *arr, void *data); + +void bpf_ir_array_free(struct array *); + +struct array bpf_ir_array_null(void); + +void bpf_ir_array_erase(struct array *arr, size_t idx); + +void *bpf_ir_array_get_void(struct array *arr, size_t idx); + +#define array_get(arr, idx, type) ((type *)bpf_ir_array_get_void(arr, idx)) + +int bpf_ir_array_clear(struct array *arr); + +int bpf_ir_array_clone(struct array *res, struct array *arr); + +#define array_for(pos, arr) \ + for (pos = ((typeof(pos))(arr.data)); \ + pos < (typeof(pos))(arr.data) + arr.num_elem; pos++) + +#define INIT_ARRAY(arr, type) bpf_ir_array_init(arr, sizeof(type)) + +/* Array End */ + +/* DBG Macro Start */ +#ifndef __KERNEL__ + +#define CRITICAL(str) \ + { \ + PRINT_LOG("%s:%d <%s> %s\n", __FILE__, __LINE__, __FUNCTION__, \ + str); \ + exit(1); \ + } + +#else + +#define CRITICAL(str) \ + { \ + panic("%s:%d <%s> %s\n", __FILE__, __LINE__, __FUNCTION__, \ + str); \ + } + +#endif + +#define RAISE_ERROR(str) \ + { \ + PRINT_LOG("%s:%d <%s> %s\n", __FILE__, __LINE__, __FUNCTION__, \ + str); \ + return -ENOSYS; \ + } + +#define DBGASSERT(cond) \ + if (!(cond)) { \ + CRITICAL("Assertion failed"); \ + } + +/* DBG Macro End */ + +/* */ + +void *malloc_proto(size_t size); + +void free_proto(void *ptr); + +#define SAFE_MALLOC(dst, size) \ + { \ + dst = malloc_proto(size); \ + if (!dst) { \ + return -ENOMEM; \ + } \ + } + +#define MAX_FUNC_ARG 5 + +enum imm_type { IMM, IMM64 }; + +/** + Pre-IR instructions, similar to `bpf_insn` + */ +struct pre_ir_insn { + __u8 opcode; + + __u8 dst_reg; + __u8 src_reg; + __s16 off; + + enum imm_type it; + __s32 imm; + __s64 imm64; // Immediate constant for 64-bit immediate + + size_t pos; // Original position +}; + +enum ir_value_type { + IR_VALUE_CONSTANT, + IR_VALUE_CONSTANT_RAWOFF, // A constant value in raw operations to be added during code + // generation + IR_VALUE_INSN, + IR_VALUE_STACK_PTR, + IR_VALUE_UNDEF, +}; + +/** + VALUE = CONSTANT | INSN + + "r1 = constant" pattern will use `CONSTANT` which will not be added to BB. + */ +struct ir_value { + union { + __s64 constant_d; + struct ir_insn *insn_d; + } data; + enum ir_value_type type; +}; + +/** + Value plus an offset + */ +struct ir_address_value { + // The value might be stack pointer + struct ir_value value; + __s16 offset; +}; + +/** + A single phi value entry + */ +struct phi_value { + struct ir_value value; + struct ir_basic_block *bb; +}; + +enum ir_alu_type { + IR_ALU_UNKNOWN, // To prevent from not manually setting this type + IR_ALU_32, + IR_ALU_64, +}; + +int bpf_ir_valid_alu_type(enum ir_alu_type type); + +/** + Virtual Register Type + */ +enum ir_vr_type { + IR_VR_TYPE_UNKNOWN, // To prevent from not manually setting this type + IR_VR_TYPE_8, + IR_VR_TYPE_16, + IR_VR_TYPE_32, + IR_VR_TYPE_64, +}; + +int bpf_ir_valid_vr_type(enum ir_vr_type type); + +enum ir_insn_type { + IR_INSN_ALLOC, + IR_INSN_STORE, + IR_INSN_LOAD, + IR_INSN_STORERAW, + IR_INSN_LOADRAW, + // ALU + IR_INSN_ADD, + IR_INSN_SUB, + IR_INSN_MUL, + IR_INSN_LSH, + IR_INSN_MOD, + // CALL EXIT + IR_INSN_CALL, + IR_INSN_RET, + // JMP + IR_INSN_JA, + IR_INSN_JEQ, + IR_INSN_JGT, + IR_INSN_JGE, + IR_INSN_JLT, + IR_INSN_JLE, + IR_INSN_JNE, + // PHI + IR_INSN_PHI, + // Code-gen instructions + IR_INSN_ASSIGN, + IR_INSN_REG, + // Special instructions + IR_INSN_FUNCTIONARG, // The function argument store, not an actual instruction +}; + +/** + INSN = + ALLOC + | STORE , + | LOAD + | STORERAW , + | LOADRAW + + | ADD , + | SUB , + | MUL , + | LSH , + | MOD , + | CALL + | RET + | JA + | JEQ , , , + | JGT , , , + | JGE , , , + | JLT , , , + | JLE , , , + | JNE , , , + | PHI + (For code gen usage) + | ASSIGN + | REG + (For special usage) + | FUNCTIONARG + + Note. must be the next basic block. + ASSIGN dst cannot be callee-saved registers + */ +struct ir_insn { + struct ir_value values[MAX_FUNC_ARG]; + __u8 value_num; + + // Used in ALLOC and instructions + enum ir_vr_type vr_type; + + // Used in RAW instructions + struct ir_address_value addr_val; + + // ALU Type + enum ir_alu_type alu; + + // Used in JMP instructions + struct ir_basic_block *bb1; + struct ir_basic_block *bb2; + + // Array of phi_value + struct array phi; + + __s32 fid; + enum ir_insn_type op; + + // Linked list + struct list_head list_ptr; + + // Parent BB + struct ir_basic_block *parent_bb; + + // Array of struct ir_insn * + // Users + struct array users; + + // Used when generating the real code + size_t _insn_id; + void *user_data; + __u8 _visited; +}; + +/** + Pre-IR BB + + This includes many data structures needed to generate the IR. + */ +struct pre_ir_basic_block { + // An ID used to debug + size_t id; + + // Start position in the original insns + size_t start_pos; + + // End position in the original insns + size_t end_pos; + + // The number of instructions in this basic block (modified length) + size_t len; + + struct pre_ir_insn *pre_insns; + + struct array preds; + struct array succs; + + __u8 visited; + + __u8 sealed; + __u8 filled; + struct ir_basic_block *ir_bb; + struct ir_insn *incompletePhis[MAX_BPF_REG]; +}; + +/** + IR Basic Block + */ +struct ir_basic_block { + struct list_head ir_insn_head; + + // Array of struct ir_basic_block * + struct array preds; + + // Array of struct ir_basic_block * + struct array succs; + + // Used for construction and debugging + __u8 _visited; + size_t _id; + void *user_data; + + // Array of struct ir_insn * + struct array users; +}; + +/** + The BB value used in currentDef + */ +struct bb_val { + struct pre_ir_basic_block *bb; + struct ir_value val; +}; + +/** + BB with the raw entrance position + */ +struct bb_entrance_info { + size_t entrance; + struct pre_ir_basic_block *bb; +}; + +/** + Generated BB information + */ +struct bb_info { + struct pre_ir_basic_block *entry; + + // Array of bb_entrance_info + struct array all_bbs; +}; + +/** + The environment data for transformation + */ +struct ssa_transform_env { + // Array of bb_val (which is (BB, Value) pair) + struct array currentDef[MAX_BPF_REG]; + struct bb_info info; + + // Stack pointer (r10) users + struct array sp_users; + // Function argument + struct ir_insn *function_arg[MAX_FUNC_ARG]; +}; + +struct error { + __u8 is_kernel_err : 1; + unsigned int errorno : 31; +}; + +// Helper functions + +struct ir_basic_block *bpf_ir_init_bb_raw(void); + +// Main interface +int bpf_ir_run(struct bpf_insn *insns, size_t len); + +/* Fun Start */ + +struct code_gen_info { + // All vertex in interference graph + // Array of struct ir_insn* + struct array all_var; + + // BPF Register Virtual Instruction (used as dst) + struct ir_insn *regs[MAX_BPF_REG]; + + size_t callee_num; + + __s16 stack_offset; + + // Number of instructions + size_t prog_size; + + struct bpf_insn *prog; + + // Whether to spill callee saved registers + __u8 spill_callee; +}; + +struct ir_function { + size_t arg_num; + + // Array of struct ir_basic_block * + struct array all_bbs; + + // The entry block + struct ir_basic_block *entry; + + // Store any information about the function + struct array reachable_bbs; + + // BBs who has no successors + struct array end_bbs; + + // Stack pointer (r10) users. Should be readonly. No more manual stack access should be allowed. + struct array sp_users; + + // Function argument + struct ir_insn *function_arg[MAX_FUNC_ARG]; + + // Array of struct ir_constraint. Value constraints. + struct array value_constraints; + + struct code_gen_info cg_info; +}; + +// IR checks + +void bpf_ir_prog_check(struct ir_function *fun); + +/* Fun End */ + +/* BB Start */ + +/// Get the number of instructions in a basic block +size_t bpf_ir_bb_len(struct ir_basic_block *); + +struct ir_bb_cg_extra *bpf_ir_bb_cg(struct ir_basic_block *bb); + +struct ir_basic_block *bpf_ir_create_bb(struct ir_function *fun); + +void bpf_ir_connect_bb(struct ir_basic_block *from, struct ir_basic_block *to); + +void bpf_ir_disconnect_bb(struct ir_basic_block *from, + struct ir_basic_block *to); + +/// Split a BB after an instruction +struct ir_basic_block *bpf_ir_split_bb(struct ir_function *fun, + struct ir_insn *insn); + +struct ir_insn *bpf_ir_get_last_insn(struct ir_basic_block *bb); + +struct ir_insn *bpf_ir_get_first_insn(struct ir_basic_block *bb); + +int bpf_ir_bb_empty(struct ir_basic_block *bb); + +/* BB End */ + +/* IR Helper Start */ + +void clean_env_all(struct ir_function *fun); + +void print_ir_prog(struct ir_function *); + +void print_ir_prog_reachable(struct ir_function *fun); + +void print_ir_prog_advanced(struct ir_function *, + void (*)(struct ir_basic_block *), + void (*)(struct ir_insn *), + void (*)(struct ir_insn *)); + +void print_ir_dst(struct ir_insn *insn); + +void print_ir_alloc(struct ir_insn *insn); + +void clean_env(struct ir_function *); + +// Tag the instruction and BB +void tag_ir(struct ir_function *fun); + +// Remove all tag information +void clean_tag(struct ir_function *); + +void print_address_value(struct ir_address_value v); + +void print_vr_type(enum ir_vr_type t); + +void print_phi(struct array *phi); + +void assign_id(struct ir_basic_block *bb, size_t *cnt, size_t *bb_cnt); + +void print_ir_insn(struct ir_insn *); + +void print_ir_value(struct ir_value v); + +void print_raw_ir_insn(struct ir_insn *insn); + +void print_raw_ir_bb(struct ir_basic_block *bb); + +void print_insn_ptr_base(struct ir_insn *insn); + +void print_ir_err_init(struct ir_function *fun); + +void print_ir_insn_err(struct ir_insn *insn, char *msg); + +void print_ir_bb_err(struct ir_basic_block *bb); + +/* IR Helper End */ + +/* IR Instructions Start */ + +enum insert_position { + INSERT_BACK, + INSERT_FRONT, + // BB-specific + INSERT_BACK_BEFORE_JMP, + INSERT_FRONT_AFTER_PHI +}; + +// Return an array of struct ir_value* +struct array get_operands(struct ir_insn *insn); + +void replace_all_usage(struct ir_insn *insn, struct ir_value rep); + +void replace_all_usage_except(struct ir_insn *insn, struct ir_value rep, + struct ir_insn *except); + +void erase_insn(struct ir_insn *insn); + +int is_last_insn(struct ir_insn *insn); + +// Erase an instruction without checking the users +// Used in code gen +void erase_insn_raw(struct ir_insn *insn); + +int is_void(struct ir_insn *insn); + +int is_jmp(struct ir_insn *insn); + +int is_cond_jmp(struct ir_insn *insn); + +int is_alu(struct ir_insn *insn); + +struct ir_insn *prev_insn(struct ir_insn *insn); + +struct ir_insn *next_insn(struct ir_insn *insn); + +struct ir_insn *create_alloc_insn(struct ir_insn *insn, enum ir_vr_type type, + enum insert_position pos); + +struct ir_insn *create_alloc_insn_bb(struct ir_basic_block *bb, + enum ir_vr_type type, + enum insert_position pos); + +struct ir_insn *create_store_insn(struct ir_insn *insn, struct ir_insn *st_insn, + struct ir_value val, + enum insert_position pos); + +struct ir_insn *create_store_insn_bb(struct ir_basic_block *bb, + struct ir_insn *st_insn, + struct ir_value val, + enum insert_position pos); + +struct ir_insn *create_load_insn(struct ir_insn *insn, struct ir_value val, + enum insert_position pos); + +struct ir_insn *create_load_insn_bb(struct ir_basic_block *bb, + struct ir_value val, + enum insert_position pos); + +struct ir_insn *create_bin_insn(struct ir_insn *insn, struct ir_value val1, + struct ir_value val2, enum ir_insn_type ty, + enum ir_alu_type aluty, + enum insert_position pos); + +struct ir_insn *create_bin_insn_bb(struct ir_basic_block *bb, + struct ir_value val1, struct ir_value val2, + enum ir_insn_type ty, enum ir_alu_type aluty, + enum insert_position pos); + +struct ir_insn *create_ja_insn(struct ir_insn *insn, + struct ir_basic_block *to_bb, + enum insert_position pos); + +struct ir_insn *create_ja_insn_bb(struct ir_basic_block *bb, + struct ir_basic_block *to_bb, + enum insert_position pos); + +struct ir_insn *create_jbin_insn(struct ir_insn *insn, struct ir_value val1, + struct ir_value val2, + struct ir_basic_block *to_bb1, + struct ir_basic_block *to_bb2, + enum ir_insn_type ty, enum ir_alu_type aluty, + enum insert_position pos); + +struct ir_insn * +create_jbin_insn_bb(struct ir_basic_block *bb, struct ir_value val1, + struct ir_value val2, struct ir_basic_block *to_bb1, + struct ir_basic_block *to_bb2, enum ir_insn_type ty, + enum ir_alu_type aluty, enum insert_position pos); + +struct ir_insn *create_ret_insn(struct ir_insn *insn, struct ir_value val, + enum insert_position pos); + +struct ir_insn *create_ret_insn_bb(struct ir_basic_block *bb, + struct ir_value val, + enum insert_position pos); + +struct ir_insn *create_assign_insn(struct ir_insn *insn, struct ir_value val, + enum insert_position pos); + +struct ir_insn *create_assign_insn_bb(struct ir_basic_block *bb, + struct ir_value val, + enum insert_position pos); + +struct ir_insn *create_phi_insn(struct ir_insn *insn, enum insert_position pos); + +struct ir_insn *create_phi_insn_bb(struct ir_basic_block *bb, + enum insert_position pos); + +void phi_add_operand(struct ir_insn *insn, struct ir_basic_block *bb, + struct ir_value val); + +void val_add_user(struct ir_value val, struct ir_insn *user); + +void val_remove_user(struct ir_value val, struct ir_insn *user); + +struct ir_insn *create_assign_insn_cg(struct ir_insn *insn, struct ir_value val, + enum insert_position pos); + +struct ir_insn *create_assign_insn_bb_cg(struct ir_basic_block *bb, + struct ir_value val, + enum insert_position pos); + +void replace_operand(struct ir_insn *insn, struct ir_value v1, + struct ir_value v2); + +struct ir_insn *create_insn_base_cg(struct ir_basic_block *bb); + +struct ir_insn *create_insn_base(struct ir_basic_block *bb); + +void insert_at(struct ir_insn *new_insn, struct ir_insn *insn, + enum insert_position pos); + +void insert_at_bb(struct ir_insn *new_insn, struct ir_basic_block *bb, + enum insert_position pos); + +/* IR Instructions End */ + +/* Passes Start */ + +void remove_trivial_phi(struct ir_function *fun); + +void cut_bb(struct ir_function *fun); + +void add_counter(struct ir_function *fun); + +void add_constraint(struct ir_function *fun); + +struct function_pass { + void (*pass)(struct ir_function *); + char name[30]; +}; + +#define DEF_FUNC_PASS(fun, msg) { .pass = fun, .name = msg } + +/* Passes End */ + +/* Code Gen Start */ + +int bpf_ir_init_insn_cg(struct ir_insn *insn); + +int bpf_ir_code_gen(struct ir_function *fun); + +// Extra information needed for code gen +struct ir_bb_cg_extra { + // Position of the first instruction + size_t pos; +}; + +struct ir_insn_cg_extra { + // Destination (Not in SSA form anymore) + struct ir_insn *dst; + + // Liveness analysis + struct array in; + struct array out; + struct array gen; + struct array kill; + + // Adj list in interference graph + // Array of struct ir_insn* + struct array adj; + + // Translated pre_ir_insn + struct pre_ir_insn translated[2]; + + // Translated number + __u8 translated_num; + + // Whether the VR is allocated with a real register + // If it's a pre-colored register, it's also 1 + __u8 allocated; + + // When allocating register, whether dst will be spilled + // 0: Not spilled + // 1: Spilled on stack position 1 + // etc. + size_t spilled; + + // Valid if spilled == 0 && allocated == 1 + // Valid number: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 + __u8 alloc_reg; +}; + +enum val_type { + UNDEF, + REG, + CONST, + STACK, +}; + +#define insn_cg(insn) ((struct ir_insn_cg_extra *)(insn)->user_data) + +#define insn_dst(insn) insn_cg(insn)->dst + +/* Code Gen End */ + +/* Constraint Start */ + +enum constraint_type { + CONSTRAINT_TYPE_VALUE_EQUAL, + CONSTRAINT_TYPE_VALUE_RANGE +}; + +struct ir_constraint { + enum constraint_type type; + + // Range: [start, end) + struct ir_value start; + struct ir_value end; + + // Constrain value + struct ir_value cval; + + // Real value to be compared + struct ir_value val; + struct ir_insn *pos; +}; + +/* Constraint End */ + +/* IR Value Start */ + +__u8 bpf_ir_value_equal(struct ir_value a, struct ir_value b); + +struct ir_value bpf_ir_value_insn(struct ir_insn *); + +struct ir_value bpf_ir_value_stack_ptr(void); + +/* IR Value End */ + +#endif diff --git a/IR/include/list.h b/IR/include/linux/list.h similarity index 51% rename from IR/include/list.h rename to IR/include/linux/list.h index 2401e870..c561c7d8 100644 --- a/IR/include/list.h +++ b/IR/include/linux/list.h @@ -34,11 +34,11 @@ * @param member the name of the member within the struct. * */ -#define container_of(ptr, type, member) \ - ({ \ - const typeof(((type *)0)->member) *__mptr = (ptr); \ - (type *)((char *)__mptr - offsetof(type, member)); \ - }) +#define container_of(ptr, type, member) \ + ({ \ + const typeof(((type *)0)->member) *__mptr = (ptr); \ + (type *)((char *)__mptr - offsetof(type, member)); \ + }) /*@}*/ /* @@ -59,18 +59,18 @@ * using the generic single-entry routines. */ struct list_head { - struct list_head *next, *prev; + struct list_head *next, *prev; }; -#define LIST_HEAD_INIT(name) {&(name), &(name)} +#define LIST_HEAD_INIT(name) { &(name), &(name) } #define LIST_HEAD(name) struct list_head name = LIST_HEAD_INIT(name) -#define INIT_LIST_HEAD(ptr) \ - do { \ - (ptr)->next = (ptr); \ - (ptr)->prev = (ptr); \ - } while (0) +#define INIT_LIST_HEAD(ptr) \ + do { \ + (ptr)->next = (ptr); \ + (ptr)->prev = (ptr); \ + } while (0) /* * Insert a new entry between two known consecutive entries. @@ -79,11 +79,12 @@ struct list_head { * the prev/next entries already! */ static inline void __list_add(struct list_head *newl, struct list_head *prev, - struct list_head *next) { - next->prev = newl; - newl->next = next; - newl->prev = prev; - prev->next = newl; + struct list_head *next) +{ + next->prev = newl; + newl->next = next; + newl->prev = prev; + prev->next = newl; } /** @@ -94,8 +95,9 @@ static inline void __list_add(struct list_head *newl, struct list_head *prev, * Insert a new entry after the specified head. * This is good for implementing stacks. */ -static inline void list_add(struct list_head *newl, struct list_head *head) { - __list_add(newl, head, head->next); +static inline void list_add(struct list_head *newl, struct list_head *head) +{ + __list_add(newl, head, head->next); } /** @@ -106,8 +108,9 @@ static inline void list_add(struct list_head *newl, struct list_head *head) { * Insert a new entry before the specified head. * This is useful for implementing queues. */ -static inline void list_add_tail(struct list_head *newl, struct list_head *head) { - __list_add(newl, head->prev, head); +static inline void list_add_tail(struct list_head *newl, struct list_head *head) +{ + __list_add(newl, head->prev, head); } /* @@ -117,9 +120,10 @@ static inline void list_add_tail(struct list_head *newl, struct list_head *head) * This is only for internal list manipulation where we know * the prev/next entries already! */ -static inline void __list_del(struct list_head *prev, struct list_head *next) { - next->prev = prev; - prev->next = next; +static inline void __list_del(struct list_head *prev, struct list_head *next) +{ + next->prev = prev; + prev->next = next; } /** @@ -128,19 +132,21 @@ static inline void __list_del(struct list_head *prev, struct list_head *next) { * Note: list_empty on entry does not return true after this, the entry is * in an undefined state. */ -static inline void list_del(struct list_head *entry) { - __list_del(entry->prev, entry->next); - entry->next = LIST_POISON1; - entry->prev = LIST_POISON2; +static inline void list_del(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + entry->next = LIST_POISON1; + entry->prev = LIST_POISON2; } /** * list_del_init - deletes entry from list and reinitialize it. * @entry: the element to delete from the list. */ -static inline void list_del_init(struct list_head *entry) { - __list_del(entry->prev, entry->next); - INIT_LIST_HEAD(entry); +static inline void list_del_init(struct list_head *entry) +{ + __list_del(entry->prev, entry->next); + INIT_LIST_HEAD(entry); } /** @@ -148,9 +154,10 @@ static inline void list_del_init(struct list_head *entry) { * @list: the entry to move * @head: the head that will precede our entry */ -static inline void list_move(struct list_head *list, struct list_head *head) { - __list_del(list->prev, list->next); - list_add(list, head); +static inline void list_move(struct list_head *list, struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add(list, head); } /** @@ -158,29 +165,33 @@ static inline void list_move(struct list_head *list, struct list_head *head) { * @list: the entry to move * @head: the head that will follow our entry */ -static inline void list_move_tail(struct list_head *list, struct list_head *head) { - __list_del(list->prev, list->next); - list_add_tail(list, head); +static inline void list_move_tail(struct list_head *list, + struct list_head *head) +{ + __list_del(list->prev, list->next); + list_add_tail(list, head); } /** * list_empty - tests whether a list is empty * @head: the list to test. */ -static inline int list_empty(const struct list_head *head) { - return head->next == head; +static inline int list_empty(const struct list_head *head) +{ + return head->next == head; } -static inline void __list_splice(struct list_head *list, struct list_head *head) { - struct list_head *first = list->next; - struct list_head *last = list->prev; - struct list_head *at = head->next; +static inline void __list_splice(struct list_head *list, struct list_head *head) +{ + struct list_head *first = list->next; + struct list_head *last = list->prev; + struct list_head *at = head->next; - first->prev = head; - head->next = first; + first->prev = head; + head->next = first; - last->next = at; - at->prev = last; + last->next = at; + at->prev = last; } /** @@ -188,9 +199,10 @@ static inline void __list_splice(struct list_head *list, struct list_head *head) * @list: the new list to add. * @head: the place to add it in the first list. */ -static inline void list_splice(struct list_head *list, struct list_head *head) { - if (!list_empty(list)) - __list_splice(list, head); +static inline void list_splice(struct list_head *list, struct list_head *head) +{ + if (!list_empty(list)) + __list_splice(list, head); } /** @@ -200,11 +212,13 @@ static inline void list_splice(struct list_head *list, struct list_head *head) { * * The list at @list is reinitialised */ -static inline void list_splice_init(struct list_head *list, struct list_head *head) { - if (!list_empty(list)) { - __list_splice(list, head); - INIT_LIST_HEAD(list); - } +static inline void list_splice_init(struct list_head *list, + struct list_head *head) +{ + if (!list_empty(list)) { + __list_splice(list, head); + INIT_LIST_HEAD(list); + } } /** @@ -221,7 +235,8 @@ static inline void list_splice_init(struct list_head *list, struct list_head *he * @head: the head for your list. */ -#define list_for_each(pos, head) for (pos = (head)->next; pos != (head); pos = pos->next) +#define list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) /** * __list_for_each - iterate over a list @@ -233,15 +248,17 @@ static inline void list_splice_init(struct list_head *list, struct list_head *he * Use this for code that knows the list to be very short (empty * or 1 entry) most of the time. */ -#define __list_for_each(pos, head) for (pos = (head)->next; pos != (head); pos = pos->next) +#define __list_for_each(pos, head) \ + for (pos = (head)->next; pos != (head); pos = pos->next) /** * list_for_each_prev - iterate over a list backwards * @pos: the &struct list_head to use as a loop counter. * @head: the head for your list. */ -#define list_for_each_prev(pos, head) \ - for (pos = (head)->prev; prefetch(pos->prev), pos != (head); pos = pos->prev) +#define list_for_each_prev(pos, head) \ + for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \ + pos = pos->prev) /** * list_for_each_safe - iterate over a list safe against removal of list entry @@ -249,8 +266,9 @@ static inline void list_splice_init(struct list_head *list, struct list_head *he * @n: another &struct list_head to use as temporary storage * @head: the head for your list. */ -#define list_for_each_safe(pos, n, head) \ - for (pos = (head)->next, n = pos->next; pos != (head); pos = n, n = pos->next) +#define list_for_each_safe(pos, n, head) \ + for (pos = (head)->next, n = pos->next; pos != (head); \ + pos = n, n = pos->next) /** * list_for_each_entry - iterate over list of given type @@ -258,9 +276,10 @@ static inline void list_splice_init(struct list_head *list, struct list_head *he * @head: the head for your list. * @member: the name of the list_struct within the struct. */ -#define list_for_each_entry(pos, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member); &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member)) +#define list_for_each_entry(pos, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_reverse - iterate backwards over list of given type. @@ -268,9 +287,10 @@ static inline void list_splice_init(struct list_head *list, struct list_head *he * @head: the head for your list. * @member: the name of the list_struct within the struct. */ -#define list_for_each_entry_reverse(pos, head, member) \ - for (pos = list_entry((head)->prev, typeof(*pos), member); &pos->member != (head); \ - pos = list_entry(pos->member.prev, typeof(*pos), member)) +#define list_for_each_entry_reverse(pos, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.prev, typeof(*pos), member)) /** * list_prepare_entry - prepare a pos entry for use as a start point in @@ -279,7 +299,8 @@ static inline void list_splice_init(struct list_head *list, struct list_head *he * @head: the head of the list * @member: the name of the list_struct within the struct. */ -#define list_prepare_entry(pos, head, member) ((pos) ?: list_entry(head, typeof(*pos), member)) +#define list_prepare_entry(pos, head, member) \ + ((pos) ?: list_entry(head, typeof(*pos), member)) /** * list_for_each_entry_continue - iterate over list of given type @@ -288,9 +309,10 @@ static inline void list_splice_init(struct list_head *list, struct list_head *he * @head: the head for your list. * @member: the name of the list_struct within the struct. */ -#define list_for_each_entry_continue(pos, head, member) \ - for (pos = list_entry(pos->member.next, typeof(*pos), member); &pos->member != (head); \ - pos = list_entry(pos->member.next, typeof(*pos), member)) +#define list_for_each_entry_continue(pos, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = list_entry(pos->member.next, typeof(*pos), member)) /** * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry @@ -299,10 +321,11 @@ static inline void list_splice_init(struct list_head *list, struct list_head *he * @head: the head for your list. * @member: the name of the list_struct within the struct. */ -#define list_for_each_entry_safe(pos, n, head, member) \ - for (pos = list_entry((head)->next, typeof(*pos), member), \ - n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); pos = n, n = list_entry(n->member.next, typeof(*n), member)) +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) /** * list_for_each_entry_safe_continue - iterate over list of given type @@ -312,10 +335,11 @@ static inline void list_splice_init(struct list_head *list, struct list_head *he * @head: the head for your list. * @member: the name of the list_struct within the struct. */ -#define list_for_each_entry_safe_continue(pos, n, head, member) \ - for (pos = list_entry(pos->member.next, typeof(*pos), member), \ - n = list_entry(pos->member.next, typeof(*pos), member); \ - &pos->member != (head); pos = n, n = list_entry(n->member.next, typeof(*n), member)) +#define list_for_each_entry_safe_continue(pos, n, head, member) \ + for (pos = list_entry(pos->member.next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) /** * list_for_each_entry_safe_reverse - iterate backwards over list of given type safe against @@ -325,10 +349,11 @@ static inline void list_splice_init(struct list_head *list, struct list_head *he * @head: the head for your list. * @member: the name of the list_struct within the struct. */ -#define list_for_each_entry_safe_reverse(pos, n, head, member) \ - for (pos = list_entry((head)->prev, typeof(*pos), member), \ - n = list_entry(pos->member.prev, typeof(*pos), member); \ - &pos->member != (head); pos = n, n = list_entry(n->member.prev, typeof(*n), member)) +#define list_for_each_entry_safe_reverse(pos, n, head, member) \ + for (pos = list_entry((head)->prev, typeof(*pos), member), \ + n = list_entry(pos->member.prev, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.prev, typeof(*n), member)) /* * Double linked lists with a single pointer list head. @@ -338,88 +363,98 @@ static inline void list_splice_init(struct list_head *list, struct list_head *he */ struct hlist_head { - struct hlist_node *first; + struct hlist_node *first; }; struct hlist_node { - struct hlist_node *next, **pprev; + struct hlist_node *next, **pprev; }; -#define HLIST_HEAD_INIT {.first = NULL} -#define HLIST_HEAD(name) struct hlist_head name = {.first = NULL} +#define HLIST_HEAD_INIT { .first = NULL } +#define HLIST_HEAD(name) struct hlist_head name = { .first = NULL } #define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL) #define INIT_HLIST_NODE(ptr) ((ptr)->next = NULL, (ptr)->pprev = NULL) -static inline int hlist_unhashed(const struct hlist_node *h) { - return !h->pprev; +static inline int hlist_unhashed(const struct hlist_node *h) +{ + return !h->pprev; } -static inline int hlist_empty(const struct hlist_head *h) { - return !h->first; +static inline int hlist_empty(const struct hlist_head *h) +{ + return !h->first; } -static inline void __hlist_del(struct hlist_node *n) { - struct hlist_node *next = n->next; - struct hlist_node **pprev = n->pprev; - *pprev = next; - if (next) - next->pprev = pprev; +static inline void __hlist_del(struct hlist_node *n) +{ + struct hlist_node *next = n->next; + struct hlist_node **pprev = n->pprev; + *pprev = next; + if (next) + next->pprev = pprev; } -static inline void hlist_del(struct hlist_node *n) { - __hlist_del(n); - n->next = LIST_POISON1; - n->pprev = LIST_POISON2; +static inline void hlist_del(struct hlist_node *n) +{ + __hlist_del(n); + n->next = LIST_POISON1; + n->pprev = LIST_POISON2; } -static inline void hlist_del_init(struct hlist_node *n) { - if (n->pprev) { - __hlist_del(n); - INIT_HLIST_NODE(n); - } +static inline void hlist_del_init(struct hlist_node *n) +{ + if (n->pprev) { + __hlist_del(n); + INIT_HLIST_NODE(n); + } } -static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) { - struct hlist_node *first = h->first; - n->next = first; - if (first) - first->pprev = &n->next; - h->first = n; - n->pprev = &h->first; +static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h) +{ + struct hlist_node *first = h->first; + n->next = first; + if (first) + first->pprev = &n->next; + h->first = n; + n->pprev = &h->first; } /* next must be != NULL */ -static inline void hlist_add_before(struct hlist_node *n, struct hlist_node *next) { - n->pprev = next->pprev; - n->next = next; - next->pprev = &n->next; - *(n->pprev) = n; +static inline void hlist_add_before(struct hlist_node *n, + struct hlist_node *next) +{ + n->pprev = next->pprev; + n->next = next; + next->pprev = &n->next; + *(n->pprev) = n; } -static inline void hlist_add_after(struct hlist_node *n, struct hlist_node *next) { - next->next = n->next; - n->next = next; - next->pprev = &n->next; +static inline void hlist_add_after(struct hlist_node *n, + struct hlist_node *next) +{ + next->next = n->next; + n->next = next; + next->pprev = &n->next; - if (next->next) - next->next->pprev = &next->next; + if (next->next) + next->next->pprev = &next->next; } #define hlist_entry(ptr, type, member) container_of(ptr, type, member) -#define hlist_for_each(pos, head) \ - for (pos = (head)->first; pos && ({ \ - prefetch(pos->next); \ - 1; \ - }); \ - pos = pos->next) +#define hlist_for_each(pos, head) \ + for (pos = (head)->first; pos && ({ \ + prefetch(pos->next); \ + 1; \ + }); \ + pos = pos->next) -#define hlist_for_each_safe(pos, n, head) \ - for (pos = (head)->first; pos && ({ \ - n = pos->next; \ - 1; \ - }); \ - pos = n) +#define hlist_for_each_safe(pos, n, head) \ + for (pos = (head)->first; pos && ({ \ + n = pos->next; \ + 1; \ + }); \ + pos = n) /** * hlist_for_each_entry - iterate over list of given type @@ -428,16 +463,17 @@ static inline void hlist_add_after(struct hlist_node *n, struct hlist_node *next * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ -#define hlist_for_each_entry(tpos, pos, head, member) \ - for (pos = (head)->first; pos && ({ \ - prefetch(pos->next); \ - 1; \ - }) && \ - ({ \ - tpos = hlist_entry(pos, typeof(*tpos), member); \ - 1; \ - }); \ - pos = pos->next) +#define hlist_for_each_entry(tpos, pos, head, member) \ + for (pos = (head)->first; \ + pos && ({ \ + prefetch(pos->next); \ + 1; \ + }) && \ + ({ \ + tpos = hlist_entry(pos, typeof(*tpos), member); \ + 1; \ + }); \ + pos = pos->next) /** * hlist_for_each_entry_continue - iterate over a hlist continuing after existing point @@ -445,16 +481,17 @@ static inline void hlist_add_after(struct hlist_node *n, struct hlist_node *next * @pos: the &struct hlist_node to use as a loop counter. * @member: the name of the hlist_node within the struct. */ -#define hlist_for_each_entry_continue(tpos, pos, member) \ - for (pos = (pos)->next; pos && ({ \ - prefetch(pos->next); \ - 1; \ - }) && \ - ({ \ - tpos = hlist_entry(pos, typeof(*tpos), member); \ - 1; \ - }); \ - pos = pos->next) +#define hlist_for_each_entry_continue(tpos, pos, member) \ + for (pos = (pos)->next; \ + pos && ({ \ + prefetch(pos->next); \ + 1; \ + }) && \ + ({ \ + tpos = hlist_entry(pos, typeof(*tpos), member); \ + 1; \ + }); \ + pos = pos->next) /** * hlist_for_each_entry_from - iterate over a hlist continuing from existing point @@ -462,16 +499,16 @@ static inline void hlist_add_after(struct hlist_node *n, struct hlist_node *next * @pos: the &struct hlist_node to use as a loop counter. * @member: the name of the hlist_node within the struct. */ -#define hlist_for_each_entry_from(tpos, pos, member) \ - for (; pos && ({ \ - prefetch(pos->next); \ - 1; \ - }) && \ - ({ \ - tpos = hlist_entry(pos, typeof(*tpos), member); \ - 1; \ - }); \ - pos = pos->next) +#define hlist_for_each_entry_from(tpos, pos, member) \ + for (; pos && ({ \ + prefetch(pos->next); \ + 1; \ + }) && \ + ({ \ + tpos = hlist_entry(pos, typeof(*tpos), member); \ + 1; \ + }); \ + pos = pos->next) /** * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry @@ -481,15 +518,16 @@ static inline void hlist_add_after(struct hlist_node *n, struct hlist_node *next * @head: the head for your list. * @member: the name of the hlist_node within the struct. */ -#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ - for (pos = (head)->first; pos && ({ \ - n = pos->next; \ - 1; \ - }) && \ - ({ \ - tpos = hlist_entry(pos, typeof(*tpos), member); \ - 1; \ - }); \ - pos = n) +#define hlist_for_each_entry_safe(tpos, pos, n, head, member) \ + for (pos = (head)->first; \ + pos && ({ \ + n = pos->next; \ + 1; \ + }) && \ + ({ \ + tpos = hlist_entry(pos, typeof(*tpos), member); \ + 1; \ + }); \ + pos = n) #endif diff --git a/IR/include/passes.h b/IR/include/passes.h deleted file mode 100644 index 8f06674d..00000000 --- a/IR/include/passes.h +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef __BPF_IR_PASSES_H__ -#define __BPF_IR_PASSES_H__ - -#include "ir_fun.h" - -void remove_trivial_phi(struct ir_function *fun); - -void cut_bb(struct ir_function *fun); - -void add_counter(struct ir_function *fun); - -void add_constraint(struct ir_function *fun); - -void gen_reachable_bbs(struct ir_function *); - -void gen_end_bbs(struct ir_function *fun); - -/** - All function passes. - */ -static void (*passes[])(struct ir_function *fun) = { - remove_trivial_phi, gen_end_bbs - // add_constraint, - // add_counter, -}; - -#endif diff --git a/IR/include/prog_check.h b/IR/include/prog_check.h deleted file mode 100644 index aa54d498..00000000 --- a/IR/include/prog_check.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef __BPF_IR_PROG_CHECK_H__ -#define __BPF_IR_PROG_CHECK_H__ - -#include "ir_fun.h" - -void prog_check(struct ir_function *fun); - -void check_users(struct ir_function *fun); - -#endif diff --git a/IR/include/read.h b/IR/include/read.h deleted file mode 100644 index c0d3021c..00000000 --- a/IR/include/read.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef __READ_H__ -#define __READ_H__ -#include -#include - -void run(struct bpf_insn *, size_t); - -#endif diff --git a/IR/ir_bb.c b/IR/ir_bb.c index 93ca2c4c..9435116a 100644 --- a/IR/ir_bb.c +++ b/IR/ir_bb.c @@ -1,74 +1,114 @@ -#include "ir_bb.h" -#include "array.h" -#include "bpf_ir.h" +#include -size_t bb_len(struct ir_basic_block *bb) { - size_t len = 0; - struct list_head *p = NULL; - list_for_each(p, &bb->ir_insn_head) { - len++; - } - return len; +size_t bpf_ir_bb_len(struct ir_basic_block *bb) +{ + size_t len = 0; + struct list_head *p = NULL; + list_for_each(p, &bb->ir_insn_head) { + len++; + } + return len; } -int bb_empty(struct ir_basic_block *bb) { - return list_empty(&bb->ir_insn_head); +int bpf_ir_bb_empty(struct ir_basic_block *bb) +{ + return list_empty(&bb->ir_insn_head); } -struct ir_basic_block *create_bb(struct ir_function *fun) { - struct ir_basic_block *new_bb = init_ir_bb_raw(); - array_push(&fun->all_bbs, &new_bb); - return new_bb; +// May have exception +struct ir_basic_block *bpf_ir_init_bb_raw(void) +{ + struct ir_basic_block *new_bb = + malloc_proto(sizeof(struct ir_basic_block)); + if (!new_bb) { + return NULL; + } + INIT_LIST_HEAD(&new_bb->ir_insn_head); + new_bb->user_data = NULL; + INIT_ARRAY(&new_bb->preds, struct ir_basic_block *); + INIT_ARRAY(&new_bb->succs, struct ir_basic_block *); + INIT_ARRAY(&new_bb->users, struct ir_insn *); + return new_bb; } -void connect_bb(struct ir_basic_block *from, struct ir_basic_block *to) { - array_push_unique(&from->succs, &to); - array_push_unique(&to->preds, &from); +// May have exception +struct ir_basic_block *bpf_ir_create_bb(struct ir_function *fun) +{ + struct ir_basic_block *new_bb = bpf_ir_init_bb_raw(); + if (!new_bb) { + return NULL; + } + bpf_ir_array_push(&fun->all_bbs, &new_bb); + return new_bb; } -void disconnect_bb(struct ir_basic_block *from, struct ir_basic_block *to) { - for (size_t i = 0; i < from->succs.num_elem; ++i) { - if (((struct ir_basic_block **)(from->succs.data))[i] == to) { - array_erase(&from->succs, i); - break; - } - } - for (size_t i = 0; i < to->preds.num_elem; ++i) { - if (((struct ir_basic_block **)(to->preds.data))[i] == from) { - array_erase(&to->preds, i); - break; - } - } +void bpf_ir_connect_bb(struct ir_basic_block *from, struct ir_basic_block *to) +{ + bpf_ir_array_push_unique(&from->succs, &to); + bpf_ir_array_push_unique(&to->preds, &from); } -struct ir_basic_block *split_bb(struct ir_function *fun, struct ir_insn *insn) { - struct ir_basic_block *bb = insn->parent_bb; - struct ir_basic_block *new_bb = create_bb(fun); - new_bb->succs = bb->succs; - bb->succs = array_init(sizeof(struct ir_basic_block *)); - connect_bb(bb, new_bb); - // Move all instructions after insn to new_bb - struct list_head *p = insn->list_ptr.next; - while (p != &bb->ir_insn_head) { - struct ir_insn *cur = list_entry(p, struct ir_insn, list_ptr); - p = p->next; - list_del(&cur->list_ptr); - list_add_tail(&cur->list_ptr, &new_bb->ir_insn_head); - cur->parent_bb = new_bb; - } - return new_bb; +void bpf_ir_disconnect_bb(struct ir_basic_block *from, + struct ir_basic_block *to) +{ + for (size_t i = 0; i < from->succs.num_elem; ++i) { + if (((struct ir_basic_block **)(from->succs.data))[i] == to) { + bpf_ir_array_erase(&from->succs, i); + break; + } + } + for (size_t i = 0; i < to->preds.num_elem; ++i) { + if (((struct ir_basic_block **)(to->preds.data))[i] == from) { + bpf_ir_array_erase(&to->preds, i); + break; + } + } } -struct ir_insn *get_last_insn(struct ir_basic_block *bb) { - if (bb_empty(bb)) { - return NULL; - } - return list_entry(bb->ir_insn_head.prev, struct ir_insn, list_ptr); +struct ir_basic_block *bpf_ir_split_bb(struct ir_function *fun, + struct ir_insn *insn) +{ + struct ir_basic_block *bb = insn->parent_bb; + struct ir_basic_block *new_bb = bpf_ir_create_bb(fun); + struct array old_succs = bb->succs; + INIT_ARRAY(&bb->succs, struct ir_basic_block *); + bpf_ir_connect_bb(bb, new_bb); + struct ir_basic_block **pos = NULL; + array_for(pos, old_succs) + { + bpf_ir_disconnect_bb(bb, *pos); + bpf_ir_connect_bb(new_bb, *pos); + } + bpf_ir_array_free(&old_succs); + // Move all instructions after insn to new_bb + struct list_head *p = insn->list_ptr.next; + while (p != &bb->ir_insn_head) { + struct ir_insn *cur = list_entry(p, struct ir_insn, list_ptr); + p = p->next; + list_del(&cur->list_ptr); + list_add_tail(&cur->list_ptr, &new_bb->ir_insn_head); + cur->parent_bb = new_bb; + } + return new_bb; } -struct ir_insn *get_first_insn(struct ir_basic_block *bb) { - if (bb_empty(bb)) { - return NULL; - } - return list_entry(bb->ir_insn_head.next, struct ir_insn, list_ptr); +struct ir_insn *bpf_ir_get_last_insn(struct ir_basic_block *bb) +{ + if (bpf_ir_bb_empty(bb)) { + return NULL; + } + return list_entry(bb->ir_insn_head.prev, struct ir_insn, list_ptr); +} + +struct ir_insn *bpf_ir_get_first_insn(struct ir_basic_block *bb) +{ + if (bpf_ir_bb_empty(bb)) { + return NULL; + } + return list_entry(bb->ir_insn_head.next, struct ir_insn, list_ptr); +} + +struct ir_bb_cg_extra *bpf_ir_bb_cg(struct ir_basic_block *bb) +{ + return bb->user_data; } diff --git a/IR/ir_code_gen.c b/IR/ir_code_gen.c index 9c35416a..23ee3e1b 100644 --- a/IR/ir_code_gen.c +++ b/IR/ir_code_gen.c @@ -1,230 +1,2092 @@ -#include -#include -#include "array.h" -#include "bpf_ir.h" -#include "code_gen.h" -#include "dbg.h" -#include "ir_insn.h" -#include "list.h" -#include "passes.h" -#include "prog_check.h" -#include "ir_helper.h" - -struct ir_insn_cg_extra *init_insn_cg(struct ir_insn *insn) { - struct ir_insn_cg_extra *extra = __malloc(sizeof(struct ir_insn_cg_extra)); - // When init, the destination is itself - if (is_void(insn)) { - extra->dst = NULL; - } else { - extra->dst = insn; - } - extra->adj = INIT_ARRAY(struct ir_insn *); - extra->allocated = 0; - extra->spilled = 0; - extra->alloc_reg = 0; - extra->gen = INIT_ARRAY(struct ir_insn *); - extra->kill = INIT_ARRAY(struct ir_insn *); - extra->in = INIT_ARRAY(struct ir_insn *); - extra->out = INIT_ARRAY(struct ir_insn *); - insn->user_data = extra; - return extra; -} - -void init_cg(struct ir_function *fun) { - struct ir_basic_block **pos; - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_bb_cg_extra *bb_cg = __malloc(sizeof(struct ir_bb_cg_extra)); - // Empty bb cg - bb->user_data = bb_cg; - - struct ir_insn *insn; - list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - init_insn_cg(insn); - } - } - - for (__u8 i = 0; i < MAX_BPF_REG; ++i) { - fun->cg_info.regs[i] = __malloc(sizeof(struct ir_insn)); - // Those should be read-only - struct ir_insn *insn = fun->cg_info.regs[i]; - insn->op = IR_INSN_REG; - insn->parent_bb = NULL; - insn->users = INIT_ARRAY(struct ir_insn *); - insn->value_num = 0; - struct ir_insn_cg_extra *extra = init_insn_cg(insn); - extra->alloc_reg = i; - extra->dst = insn; - // Pre-colored registers are allocated - extra->allocated = 1; - extra->spilled = 0; - } -} - -void free_insn_cg(struct ir_insn *insn) { - struct ir_insn_cg_extra *extra = insn_cg(insn); - array_free(&extra->adj); - array_free(&extra->gen); - array_free(&extra->kill); - array_free(&extra->in); - array_free(&extra->out); - __free(extra); - insn->user_data = NULL; -} - -void free_cg_res(struct ir_function *fun) { - struct ir_basic_block **pos; - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_bb_cg_extra *bb_cg = bb->user_data; - __free(bb_cg); - bb->user_data = NULL; - struct ir_insn *insn; - list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - free_insn_cg(insn); - } - } - - for (__u8 i = 0; i < MAX_BPF_REG; ++i) { - struct ir_insn *insn = fun->cg_info.regs[i]; - array_free(&insn->users); - free_insn_cg(insn); - __free(insn); - } -} - -void clean_insn_cg(struct ir_insn *insn) { - struct ir_insn_cg_extra *extra = insn_cg(insn); - array_clear(&extra->adj); - array_clear(&extra->gen); - array_clear(&extra->kill); - array_clear(&extra->in); - array_clear(&extra->out); -} - -void clean_cg(struct ir_function *fun) { - struct ir_basic_block **pos; - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - struct ir_insn *insn; - list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - clean_insn_cg(insn); - struct ir_insn_cg_extra *extra = insn_cg(insn); - extra->allocated = 0; - extra->spilled = 0; - extra->alloc_reg = 0; - } - } - - for (__u8 i = 0; i < MAX_BPF_REG; ++i) { - struct ir_insn *insn = fun->cg_info.regs[i]; - clean_insn_cg(insn); - } - array_clear(&fun->cg_info.all_var); -} - -struct ir_insn_cg_extra *insn_cg(struct ir_insn *insn) { - return insn->user_data; -} - -struct ir_insn *dst(struct ir_insn *insn) { - return insn_cg(insn)->dst; -} - -void print_ir_prog_pre_cg(struct ir_function *fun) { - printf("-----------------\n"); - print_ir_prog_advanced(fun, NULL, NULL, NULL); -} - -void print_ir_prog_cg_dst(struct ir_function *fun) { - printf("-----------------\n"); - print_ir_prog_advanced(fun, NULL, NULL, print_ir_dst); -} - -void print_ir_prog_cg_alloc(struct ir_function *fun) { - printf("-----------------\n"); - print_ir_prog_advanced(fun, NULL, NULL, print_ir_alloc); -} - -void synthesize(struct ir_function *fun) { - // The last step, synthesizes the program -} - -void code_gen(struct ir_function *fun) { - // Preparation - - // Step 1: Check program - prog_check(fun); - // Step 2: Eliminate SSA - to_cssa(fun); - check_users(fun); - print_ir_prog_pre_cg(fun); +#include - // Init CG, start real code generation - init_cg(fun); - explicit_reg(fun); // Still in SSA form, users are available - print_ir_prog_pre_cg(fun); - print_ir_prog_cg_dst(fun); +static int init_cg(struct ir_function *fun) +{ + int ret = 0; + struct ir_basic_block **pos = NULL; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_bb_cg_extra *bb_cg = NULL; + SAFE_MALLOC(bb_cg, sizeof(struct ir_bb_cg_extra)); + // Empty bb cg + bb->user_data = bb_cg; - // SSA Destruction - // users not available from now on + struct ir_insn *insn = NULL; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + ret = bpf_ir_init_insn_cg(insn); + if (ret) { + return ret; + } + } + } - remove_phi(fun); - print_ir_prog_cg_dst(fun); + for (__u8 i = 0; i < MAX_BPF_REG; ++i) { + SAFE_MALLOC(fun->cg_info.regs[i], sizeof(struct ir_insn)); + // Those should be read-only + struct ir_insn *insn = fun->cg_info.regs[i]; + insn->op = IR_INSN_REG; + insn->parent_bb = NULL; + INIT_ARRAY(&insn->users, struct ir_insn *); + insn->value_num = 0; + ret = bpf_ir_init_insn_cg(insn); + if (ret) { + return ret; + } + struct ir_insn_cg_extra *extra = insn_cg(insn); + extra->alloc_reg = i; + extra->dst = insn; + // Pre-colored registers are allocated + extra->allocated = 1; + extra->spilled = 0; + } + return 0; +} + +static void free_insn_cg(struct ir_insn *insn) +{ + struct ir_insn_cg_extra *extra = insn_cg(insn); + bpf_ir_array_free(&extra->adj); + bpf_ir_array_free(&extra->gen); + bpf_ir_array_free(&extra->kill); + bpf_ir_array_free(&extra->in); + bpf_ir_array_free(&extra->out); + free_proto(extra); + insn->user_data = NULL; +} + +static void free_cg_res(struct ir_function *fun) +{ + struct ir_basic_block **pos = NULL; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_bb_cg_extra *bb_cg = bb->user_data; + free_proto(bb_cg); + bb->user_data = NULL; + struct ir_insn *insn = NULL; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + free_insn_cg(insn); + } + } + + for (__u8 i = 0; i < MAX_BPF_REG; ++i) { + struct ir_insn *insn = fun->cg_info.regs[i]; + bpf_ir_array_free(&insn->users); + free_insn_cg(insn); + free_proto(insn); + } +} + +static void clean_insn_cg(struct ir_insn *insn) +{ + struct ir_insn_cg_extra *extra = insn_cg(insn); + bpf_ir_array_clear(&extra->adj); + bpf_ir_array_clear(&extra->gen); + bpf_ir_array_clear(&extra->kill); + bpf_ir_array_clear(&extra->in); + bpf_ir_array_clear(&extra->out); +} + +static void clean_cg(struct ir_function *fun) +{ + struct ir_basic_block **pos = NULL; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn = NULL; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + clean_insn_cg(insn); + struct ir_insn_cg_extra *extra = insn_cg(insn); + extra->allocated = 0; + extra->spilled = 0; + extra->alloc_reg = 0; + } + } + + for (__u8 i = 0; i < MAX_BPF_REG; ++i) { + struct ir_insn *insn = fun->cg_info.regs[i]; + clean_insn_cg(insn); + } + bpf_ir_array_clear(&fun->cg_info.all_var); +} + +static void print_ir_prog_pre_cg(struct ir_function *fun, char *msg) +{ + PRINT_LOG("\x1B[32m----- CG: %s -----\x1B[0m\n", msg); + print_ir_prog_advanced(fun, NULL, NULL, NULL); +} + +static void print_ir_prog_cg_dst(struct ir_function *fun, char *msg) +{ + PRINT_LOG("\x1B[32m----- CG: %s -----\x1B[0m\n", msg); + print_ir_prog_advanced(fun, NULL, NULL, print_ir_dst); +} + +static void print_ir_prog_cg_alloc(struct ir_function *fun, char *msg) +{ + PRINT_LOG("\x1B[32m----- CG: %s -----\x1B[0m\n", msg); + print_ir_prog_advanced(fun, NULL, NULL, print_ir_alloc); +} + +static int synthesize(struct ir_function *fun) +{ + // The last step, synthesizes the program + SAFE_MALLOC(fun->cg_info.prog, + fun->cg_info.prog_size * sizeof(struct bpf_insn)); + struct ir_basic_block **pos = NULL; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn = NULL; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + struct ir_insn_cg_extra *extra = insn_cg(insn); + for (__u8 i = 0; i < extra->translated_num; ++i) { + struct pre_ir_insn translated_insn = + extra->translated[i]; + PRINT_LOG("Writing to insn %zu\n", + translated_insn.pos); + struct bpf_insn *real_insn = + &fun->cg_info.prog[translated_insn.pos]; + real_insn->code = translated_insn.opcode; + real_insn->dst_reg = translated_insn.dst_reg; + real_insn->src_reg = translated_insn.src_reg; + real_insn->off = translated_insn.off; + if (translated_insn.it == IMM) { + real_insn->imm = translated_insn.imm; + } else { + // Wide instruction + struct bpf_insn *real_insn2 = + &fun->cg_info.prog + [translated_insn.pos + + 1]; + real_insn->imm = translated_insn.imm64 & + 0xffffffff; + real_insn2->imm = + translated_insn.imm64 >> 32; + } + } + } + } + return 0; +} + +// Convert from TSSA to CSSA +// Using "Method I" in paper "Translating Out of Static Single Assignment Form" +static void to_cssa(struct ir_function *fun) +{ + struct array phi_insns; + INIT_ARRAY(&phi_insns, struct ir_insn *); + + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + if (insn->op == IR_INSN_PHI) { + bpf_ir_array_push(&phi_insns, &insn); + } else { + break; + } + } + } + + struct ir_insn **pos2; + array_for(pos2, phi_insns) + { + struct ir_insn *insn = *pos2; + // Create the moved PHI insn + struct ir_insn *new_phi = create_phi_insn(insn, INSERT_FRONT); + struct phi_value *pos3; + array_for(pos3, insn->phi) + { + struct ir_insn *new_insn = create_assign_insn_bb( + pos3->bb, pos3->value, INSERT_BACK_BEFORE_JMP); + // Remove use + val_remove_user(pos3->value, insn); + phi_add_operand(new_phi, pos3->bb, + bpf_ir_value_insn(new_insn)); + } + + bpf_ir_array_free(&insn->phi); + insn->op = IR_INSN_ASSIGN; + struct ir_value val = bpf_ir_value_insn(new_phi); + insn->values[0] = val; + insn->value_num = 1; + val_add_user(val, insn); + } + + bpf_ir_array_free(&phi_insns); +} + +// Remove PHI insn +static void remove_phi(struct ir_function *fun) +{ + // dst information ready + struct array phi_insns; + INIT_ARRAY(&phi_insns, struct ir_insn *); + + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + if (insn->op == IR_INSN_PHI) { + bpf_ir_array_push(&phi_insns, &insn); + } else { + break; + } + } + } + + struct ir_insn **pos2; + array_for(pos2, phi_insns) + { + struct ir_insn *insn = *pos2; + struct ir_insn *repr = NULL; + struct phi_value *pos3; + array_for(pos3, insn->phi) + { + if (!repr) { + repr = pos3->value.data.insn_d; + } else { + insn_cg(pos3->value.data.insn_d)->dst = repr; + } + } + if (!repr) { + CRITICAL("Empty Phi not removed!"); + } + + DBGASSERT(repr == insn_dst(repr)); + + replace_all_usage(insn, bpf_ir_value_insn(repr)); + erase_insn(insn); + } + + bpf_ir_array_free(&phi_insns); +} + +static void coaleasing(struct ir_function *fun) +{ + struct ir_basic_block **pos; + // For each BB + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *pos2, *tmp; + // For each operation + list_for_each_entry_safe(pos2, tmp, &bb->ir_insn_head, + list_ptr) { + struct ir_insn *insn_dst = insn_dst(pos2); + if (pos2->op == IR_INSN_ASSIGN) { + if (pos2->values[0].type == IR_VALUE_INSN) { + struct ir_insn *src = + pos2->values[0].data.insn_d; + DBGASSERT(src == insn_dst(src)); + if (insn_cg(src)->alloc_reg == + insn_cg(insn_dst)->alloc_reg) { + // Remove + erase_insn_raw(pos2); + } + } + } + } + } +} + +static int is_insn_final(struct ir_insn *v1) +{ + return v1 == insn_dst(v1); +} + +static void build_conflict(struct ir_insn *v1, struct ir_insn *v2) +{ + if (!is_insn_final(v1) || !is_insn_final(v2)) { + CRITICAL("Can only build conflict on final values"); + } + if (v1 == v2) { + return; + } + bpf_ir_array_push_unique(&insn_cg(v1)->adj, &v2); + bpf_ir_array_push_unique(&insn_cg(v2)->adj, &v1); +} + +static void bpf_ir_print_interference_graph(struct ir_function *fun) +{ + // Tag the IR to have the actual number to print + tag_ir(fun); + struct ir_insn **pos; + array_for(pos, fun->cg_info.all_var) + { + struct ir_insn *insn = *pos; + if (insn->op == IR_INSN_REG) { + CRITICAL( + "Pre-colored register should not be in all_var"); + } + if (!is_insn_final(insn)) { + // Not final value, give up + CRITICAL("Not Final Value!"); + } + struct ir_insn_cg_extra *extra = insn_cg(insn); + if (extra->allocated) { + // Allocated VR + PRINT_LOG("%%%zu(", insn->_insn_id); + if (extra->spilled) { + PRINT_LOG("sp-%zu", extra->spilled * 8); + } else { + PRINT_LOG("r%u", extra->alloc_reg); + } + PRINT_LOG("):"); + } else { + // Pre-colored registers or unallocated VR + print_insn_ptr_base(insn); + PRINT_LOG(":"); + } + struct ir_insn **pos2; + array_for(pos2, insn_cg(insn)->adj) + { + struct ir_insn *adj_insn = *pos2; + if (!is_insn_final(adj_insn)) { + // Not final value, give up + CRITICAL("Not Final Value!"); + } + PRINT_LOG(" "); + print_insn_ptr_base(adj_insn); + } + PRINT_LOG("\n"); + } +} + +static void caller_constraint(struct ir_function *fun, struct ir_insn *insn) +{ + for (__u8 i = BPF_REG_0; i < BPF_REG_6; ++i) { + // R0-R5 are caller saved register + DBGASSERT(fun->cg_info.regs[i] == + insn_dst(fun->cg_info.regs[i])); + build_conflict(fun->cg_info.regs[i], insn); + } +} + +static void conflict_analysis(struct ir_function *fun) +{ + // Basic conflict: + // For every x in KILL set, x is conflict with every element in OUT set. + + struct ir_basic_block **pos; + // For each BB + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + // For each operation + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + struct ir_insn_cg_extra *insn_cg = insn->user_data; + if (insn->op == IR_INSN_CALL) { + // Add caller saved register constraints + struct ir_insn **pos2; + array_for(pos2, insn_cg->in) + { + DBGASSERT(*pos2 == insn_dst(*pos2)); + struct ir_insn **pos3; + array_for(pos3, insn_cg->out) + { + DBGASSERT(*pos3 == + insn_dst(*pos3)); + if (*pos2 == *pos3) { + // Live across CALL! + // PRINT_LOG("Found a VR live across CALL!\n"); + caller_constraint( + fun, *pos2); + } + } + } + } + struct ir_insn **pos2; + array_for(pos2, insn_cg->kill) + { + struct ir_insn *insn_dst = *pos2; + DBGASSERT(insn_dst == insn_dst(insn_dst)); + if (insn_dst->op != IR_INSN_REG) { + bpf_ir_array_push_unique( + &fun->cg_info.all_var, + &insn_dst); + } + struct ir_insn **pos3; + array_for(pos3, insn_cg->out) + { + DBGASSERT(*pos3 == insn_dst(*pos3)); + build_conflict(insn_dst, *pos3); + } + } + } + } +} + +static enum ir_vr_type alu_to_vr_type(enum ir_alu_type ty) +{ + if (ty == IR_ALU_32) { + return IR_VR_TYPE_32; + } else if (ty == IR_ALU_64) { + return IR_VR_TYPE_64; + } else { + CRITICAL("Error"); + } +} + +// Make register usage explicit +// Example: +// %x = add %y, %arg1 +// arg1 is r0 at the beginning of the function +// We then add a new instruction to the beginning of the function. + +static void explicit_reg(struct ir_function *fun) +{ + // fun is still in IR form + // Before this step, users are correct + // In this step we change some dsts + // We need carefully handle the users + // dsts are NOT users + // Invariant: All operands are final values + // Final value: v == dst(v) + struct ir_basic_block **pos; + // Maximum number of functions: MAX_FUNC_ARG + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + if (insn->op == IR_INSN_CALL) { + for (__u8 i = 0; i < insn->value_num; ++i) { + struct ir_value val = insn->values[i]; + struct ir_insn *new_insn = + create_assign_insn_cg( + insn, val, + INSERT_FRONT); + insn_cg(new_insn)->dst = + fun->cg_info.regs[i + 1]; + val_remove_user(val, insn); + } + insn->value_num = 0; // Remove all operands + struct ir_insn_cg_extra *extra = insn_cg(insn); + extra->dst = NULL; + if (insn->users.num_elem == 0) { + continue; + } + struct ir_insn *new_insn = create_assign_insn_cg( + insn, + bpf_ir_value_insn(fun->cg_info.regs[0]), + INSERT_BACK); + replace_all_usage(insn, + bpf_ir_value_insn(new_insn)); + } - int need_spill = 1; + if (insn->op == IR_INSN_RET) { + // ret x + // ==> + // R0 = x + // ret + struct ir_insn *new_insn = + create_assign_insn_cg(insn, + insn->values[0], + INSERT_FRONT); + val_remove_user(insn->values[0], insn); + insn_cg(new_insn)->dst = fun->cg_info.regs[0]; + insn->value_num = 0; + } + } + } + // Arg + for (__u8 i = 0; i < MAX_FUNC_ARG; ++i) { + if (fun->function_arg[i]->users.num_elem > 0) { + // Insert ASSIGN arg[i] at the beginning of the function + struct ir_insn *new_insn = create_assign_insn_bb_cg( + fun->entry, + bpf_ir_value_insn(fun->cg_info.regs[i + 1]), + INSERT_FRONT_AFTER_PHI); + replace_all_usage(fun->function_arg[i], + bpf_ir_value_insn(new_insn)); + } + } +} - while (need_spill) { - // Step 3: Liveness Analysis - liveness_analysis(fun); +static int compare_insn(const void *a, const void *b) +{ + struct ir_insn *ap = *(struct ir_insn **)a; + struct ir_insn *bp = *(struct ir_insn **)b; + return ap->_insn_id > bp->_insn_id; +} - // Step 4: Conflict Analysis - conflict_analysis(fun); - print_interference_graph(fun); - printf("-------------\n"); +static void graph_coloring(struct ir_function *fun) +{ + // Using the Chaitin's Algorithm + // Using the simple dominance heuristic (Simple traversal of BB) + tag_ir(fun); + struct array *all_var = &fun->cg_info.all_var; + qsort(all_var->data, all_var->num_elem, all_var->elem_size, + &compare_insn); + // all_var is now PEO + struct ir_insn **pos; + array_for(pos, (*all_var)) + { + // Allocate register for *pos + struct ir_insn *insn = *pos; + if (insn->op == IR_INSN_REG) { + CRITICAL( + "Pre-colored register should not be in all_var"); + } + struct ir_insn_cg_extra *extra = insn_cg(insn); + struct ir_insn **pos2; - // Step 5: Graph coloring - graph_coloring(fun); - coaleasing(fun); - print_interference_graph(fun); - print_ir_prog_cg_alloc(fun); + int used_reg[MAX_BPF_REG] = { 0 }; + struct array used_spill; + INIT_ARRAY(&used_spill, size_t); + array_for(pos2, extra->adj) + { + struct ir_insn *insn2 = *pos2; // Adj instruction + struct ir_insn_cg_extra *extra2 = insn_cg(insn2); + if (extra2->allocated) { + if (extra2->spilled) { + bpf_ir_array_push_unique( + &used_spill, &extra2->spilled); + } else { + used_reg[extra2->alloc_reg] = 1; + } + } + } + __u8 need_spill = 1; + for (__u8 i = 0; i < MAX_BPF_REG; i++) { + if (!used_reg[i]) { + extra->allocated = 1; + PRINT_LOG("Allocate r%u for %%%zu\n", i, + insn->_insn_id); + extra->alloc_reg = i; + need_spill = 0; + break; + } + } + if (need_spill) { + size_t sp = 1; + while (1) { + __u8 found = 1; + size_t *pos3; + array_for(pos3, used_spill) + { + if (*pos3 == sp) { + sp++; + found = 0; + break; + } + } + if (found) { + extra->allocated = 1; + extra->spilled = sp; + break; + } + } + } + bpf_ir_array_free(&used_spill); + } +} - // Step 6: Check if need to spill and spill - need_spill = check_need_spill(fun); - if (need_spill) { - // Still need to spill - printf("Need to spill...\n"); - clean_cg(fun); - } - } +// Live variable analysis + +static void gen_kill(struct ir_function *fun) +{ + struct ir_basic_block **pos; + // For each BB + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *pos2; + // For each operation + list_for_each_entry(pos2, &bb->ir_insn_head, list_ptr) { + struct ir_insn *insn_dst = insn_dst(pos2); + struct ir_insn_cg_extra *insn_cg = pos2->user_data; + if (!is_void(pos2) && insn_dst) { + bpf_ir_array_push_unique(&insn_cg->kill, + &insn_dst); + } + struct array value_uses = get_operands(pos2); + struct ir_value **pos3; + array_for(pos3, value_uses) + { + struct ir_value *val = *pos3; + if (val->type == IR_VALUE_INSN) { + struct ir_insn *insn = val->data.insn_d; + DBGASSERT(insn == insn_dst(insn)); + bpf_ir_array_push_unique(&insn_cg->gen, + &insn); + // array_erase_elem(&insn_cg->kill, insn); + } + } + bpf_ir_array_free(&value_uses); + } + } +} - // Register allocation finished (All registers are fixed) - printf("Register allocation finished\n"); - print_ir_prog_cg_alloc(fun); +static int array_contains(struct array *arr, struct ir_insn *insn) +{ + struct ir_insn **pos; + array_for(pos, (*arr)) + { + if (*pos == insn) { + return 1; + } + } + return 0; +} - // Step 7: Calculate stack size - calc_callee_num(fun); - calc_stack_size(fun); +static struct array array_delta(struct array *a, struct array *b) +{ + struct array res; + INIT_ARRAY(&res, struct ir_insn *); + struct ir_insn **pos; + array_for(pos, (*a)) + { + struct ir_insn *insn = *pos; + if (!array_contains(b, insn)) { + bpf_ir_array_push(&res, &insn); + } + } + return res; +} - // Step 8: Shift raw stack operations - add_stack_offset(fun, fun->cg_info.stack_offset); - print_ir_prog_cg_alloc(fun); +static void merge_array(struct array *a, struct array *b) +{ + struct ir_insn **pos; + array_for(pos, (*b)) + { + struct ir_insn *insn = *pos; + bpf_ir_array_push_unique(a, &insn); + } +} - // Step 9: Spill callee saved registers - spill_callee(fun); - print_ir_prog_cg_alloc(fun); +static int equal_set(struct array *a, struct array *b) +{ + if (a->num_elem != b->num_elem) { + return 0; + } + struct ir_insn **pos; + array_for(pos, (*a)) + { + struct ir_insn *insn = *pos; + if (!array_contains(b, insn)) { + return 0; + } + } + return 1; +} - // Step 10: Normalize - normalize(fun); - print_ir_prog_cg_alloc(fun); +static void in_out(struct ir_function *fun) +{ + int change = 1; + // For each BB + while (change) { + change = 0; + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; - // Step 11: Direct Translation - // translate(fun); + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + struct ir_insn_cg_extra *insn_cg = + insn->user_data; + struct array old_in = insn_cg->in; + bpf_ir_array_clear(&insn_cg->out); - relocate(fun); + if (bpf_ir_get_last_insn(bb) == insn) { + // Last instruction + struct ir_basic_block **pos2; + array_for(pos2, bb->succs) + { + struct ir_basic_block *bb2 = + *pos2; + if (bpf_ir_bb_empty(bb2)) { + CRITICAL( + "Found empty BB"); + } + struct ir_insn *first = + bpf_ir_get_first_insn( + bb2); + struct ir_insn_cg_extra + *insn2_cg = + first->user_data; + merge_array(&insn_cg->out, + &insn2_cg->in); + } + } else { + // Not last instruction + struct ir_insn *next_insn = list_entry( + insn->list_ptr.next, + struct ir_insn, list_ptr); + struct ir_insn_cg_extra *next_insn_cg = + next_insn->user_data; + merge_array(&insn_cg->out, + &next_insn_cg->in); + } + struct array out_kill_delta = array_delta( + &insn_cg->out, &insn_cg->kill); + bpf_ir_array_clone(&insn_cg->in, &insn_cg->gen); + merge_array(&insn_cg->in, &out_kill_delta); + // Check for change + if (!equal_set(&insn_cg->in, &old_in)) { + change = 1; + } + // Collect grabage + bpf_ir_array_free(&out_kill_delta); + bpf_ir_array_free(&old_in); + } + } + } +} - synthesize(fun); +static void print_insn_extra(struct ir_insn *insn) +{ + struct ir_insn_cg_extra *insn_cg = insn->user_data; + if (insn_cg == NULL) { + CRITICAL("NULL user data"); + } + PRINT_LOG("--\nGen:"); + struct ir_insn **pos; + array_for(pos, insn_cg->gen) + { + struct ir_insn *insn = *pos; + PRINT_LOG(" "); + print_insn_ptr_base(insn); + } + PRINT_LOG("\nKill:"); + array_for(pos, insn_cg->kill) + { + struct ir_insn *insn = *pos; + PRINT_LOG(" "); + print_insn_ptr_base(insn); + } + PRINT_LOG("\nIn:"); + array_for(pos, insn_cg->in) + { + struct ir_insn *insn = *pos; + PRINT_LOG(" "); + print_insn_ptr_base(insn); + } + PRINT_LOG("\nOut:"); + array_for(pos, insn_cg->out) + { + struct ir_insn *insn = *pos; + PRINT_LOG(" "); + print_insn_ptr_base(insn); + } + PRINT_LOG("\n-------------\n"); +} + +static void liveness_analysis(struct ir_function *fun) +{ + // TODO: Encode Calling convention into GEN KILL + gen_kill(fun); + in_out(fun); + PRINT_LOG("--------------\n"); + print_ir_prog_advanced(fun, NULL, print_insn_extra, print_ir_dst); + print_ir_prog_advanced(fun, NULL, NULL, print_ir_dst); +} + +static enum val_type vtype_insn(struct ir_insn *insn) +{ + insn = insn_dst(insn); + if (insn == NULL) { + // Void + return UNDEF; + } + struct ir_insn_cg_extra *extra = insn_cg(insn); + if (extra->spilled) { + return STACK; + } else { + return REG; + } +} + +static enum val_type vtype(struct ir_value val) +{ + if (val.type == IR_VALUE_INSN) { + return vtype_insn(val.data.insn_d); + } else if (val.type == IR_VALUE_CONSTANT) { + return CONST; + } else if (val.type == IR_VALUE_STACK_PTR) { + return REG; + } else { + CRITICAL("No such value type for dst"); + } +} + +// Normalization + +static void normalize(struct ir_function *fun) +{ + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + struct ir_value *v0 = &insn->values[0]; + struct ir_value *v1 = &insn->values[1]; + enum val_type t0 = insn->value_num >= 1 ? vtype(*v0) : + UNDEF; + enum val_type t1 = insn->value_num >= 2 ? vtype(*v1) : + UNDEF; + enum val_type tdst = vtype_insn(insn); + struct ir_insn *dst_insn = insn_dst(insn); + if (insn->op == IR_INSN_ALLOC) { + // Skip + } else if (insn->op == IR_INSN_STORE) { + // Should be converted to ASSIGN + CRITICAL("Error"); + } else if (insn->op == IR_INSN_LOAD) { + CRITICAL("Error"); + } else if (insn->op == IR_INSN_LOADRAW) { + // OK + } else if (insn->op == IR_INSN_STORERAW) { + // OK + } else if (is_alu(insn)) { + // Binary ALU + if (t0 == STACK && t1 == CONST) { + // reg1 = add stack const + // ==> + // reg1 = stack + // reg1 = add reg1 const + struct ir_insn *new_insn = + create_assign_insn_cg( + insn, *v0, + INSERT_FRONT); + insn_cg(new_insn)->dst = dst_insn; + new_insn->vr_type = + alu_to_vr_type(insn->alu); + v0->type = IR_VALUE_INSN; + v0->data.insn_d = dst_insn; + } else if (t0 == REG && t1 == REG) { + // reg1 = add reg2 reg3 + __u8 reg1 = + insn_cg(dst_insn)->alloc_reg; + __u8 reg2 = insn_cg(v0->data.insn_d) + ->alloc_reg; + __u8 reg3 = insn_cg(v1->data.insn_d) + ->alloc_reg; + if (reg1 != reg2) { + if (reg1 == reg3) { + // Exchange reg2 and reg3 + struct ir_value tmp = + *v0; + *v0 = *v1; + *v1 = tmp; + } else { + // reg1 = add reg2 reg3 + // ==> + // reg1 = reg2 + // reg1 = add reg1 reg3 + struct ir_insn *new_insn = + create_assign_insn_cg( + insn, + *v0, + INSERT_FRONT); + DBGASSERT( + dst_insn == + fun->cg_info.regs + [reg1]); + insn_cg(new_insn)->dst = + dst_insn; + v0->type = + IR_VALUE_INSN; + v0->data.insn_d = + dst_insn; + } + } + } else if (t0 == REG && t1 == CONST) { + if (insn_cg(v0->data.insn_d)->alloc_reg != + insn_cg(dst_insn)->alloc_reg) { + // reg1 = add reg2 const + // ==> + // reg1 = reg2 + // reg1 = add reg1 const + struct ir_insn *new_insn = + create_assign_insn_cg( + insn, *v0, + INSERT_FRONT); + insn_cg(new_insn)->dst = + dst_insn; + v0->type = IR_VALUE_INSN; + v0->data.insn_d = dst_insn; + } + } else { + CRITICAL("Error"); + } + } else if (insn->op == IR_INSN_ASSIGN) { + // stack = reg + // stack = const + // reg = const + // reg = stack + // reg = reg + if (tdst == STACK) { + DBGASSERT(t0 != STACK); + // Change to STORERAW + insn->op = IR_INSN_STORERAW; + insn->addr_val.value = + bpf_ir_value_stack_ptr(); + insn->addr_val.offset = + -insn_cg(dst_insn)->spilled * 8; + } else { + if (t0 == STACK) { + // Change to LOADRAW + insn->op = IR_INSN_LOADRAW; + insn->addr_val.value = + bpf_ir_value_stack_ptr(); + insn->addr_val.offset = + -insn_cg(v0->data.insn_d) + ->spilled * + 8; + } + } + } else if (insn->op == IR_INSN_RET) { + // OK + } else if (insn->op == IR_INSN_CALL) { + // OK + } else if (insn->op == IR_INSN_JA) { + // OK + } else if (insn->op >= IR_INSN_JEQ && + insn->op < IR_INSN_PHI) { + // jmp reg const/reg + // or + // jmp const/reg reg + // OK + } else { + CRITICAL("No such instruction"); + } + } + } +} + +// Relocate BB +static void calc_pos(struct ir_function *fun) +{ + // Calculate the position of each instruction & BB + size_t ipos = 0; // Instruction position + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_bb_cg_extra *bb_extra = bb->user_data; + bb_extra->pos = ipos; + struct ir_insn *insn; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + struct ir_insn_cg_extra *insn_extra = insn_cg(insn); + for (__u8 i = 0; i < insn_extra->translated_num; ++i) { + struct pre_ir_insn *translated_insn = + &insn_extra->translated[i]; + // Pos + translated_insn->pos = ipos; + if (translated_insn->it == IMM) { + ipos += 1; + } else { + ipos += 2; + } + } + } + } + fun->cg_info.prog_size = ipos; +} + +static void relocate(struct ir_function *fun) +{ + calc_pos(fun); + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + struct ir_insn_cg_extra *insn_extra = insn_cg(insn); + if (insn->op == IR_INSN_JA) { + DBGASSERT(insn_extra->translated_num == 1); + size_t target = bpf_ir_bb_cg(insn->bb1)->pos; + insn_extra->translated[0].off = + target - insn_extra->translated[0].pos - + 1; + } + if (is_cond_jmp(insn)) { + DBGASSERT(insn_extra->translated_num == 1); + size_t target = bpf_ir_bb_cg(insn->bb2)->pos; + insn_extra->translated[0].off = + target - insn_extra->translated[0].pos - + 1; + } + } + } +} + +static void load_stack_to_r0(struct ir_function *fun, struct ir_insn *insn, + struct ir_value *val, enum ir_vr_type vtype) +{ + struct ir_insn *tmp = create_assign_insn_cg(insn, *val, INSERT_FRONT); + tmp->vr_type = vtype; + insn_cg(tmp)->dst = fun->cg_info.regs[0]; + + val->type = IR_VALUE_INSN; + val->data.insn_d = fun->cg_info.regs[0]; +} + +static void add_stack_offset_vr(struct ir_function *fun, size_t num) +{ + struct ir_insn **pos; + array_for(pos, fun->cg_info.all_var) + { + struct ir_insn_cg_extra *extra = insn_cg(*pos); + if (extra->spilled > 0) { + extra->spilled += num; + } + } +} + +static void spill_callee(struct ir_function *fun) +{ + // Spill Callee saved registers if used + __u8 reg_used[MAX_BPF_REG] = { 0 }; + + struct ir_insn **pos; + array_for(pos, fun->cg_info.all_var) + { + struct ir_insn_cg_extra *extra = insn_cg(*pos); + reg_used[extra->alloc_reg] = 1; + } + size_t off = 0; + for (__u8 i = BPF_REG_6; i < BPF_REG_10; ++i) { + if (reg_used[i]) { + off++; + } + } + DBGASSERT(off == fun->cg_info.callee_num); + add_stack_offset_vr(fun, off); + off = 0; + for (__u8 i = BPF_REG_6; i < BPF_REG_10; ++i) { + // All callee saved registers + if (reg_used[i]) { + off++; + // Spill at sp-off + // struct ir_insn *st = create_assign_insn_bb_cg( + // fun->entry, ir_value_insn(fun->cg_info.regs[i]), INSERT_FRONT); + struct ir_insn *st = create_insn_base_cg(fun->entry); + insert_at_bb(st, fun->entry, INSERT_FRONT); + st->op = IR_INSN_STORERAW; + st->values[0] = bpf_ir_value_insn(fun->cg_info.regs[i]); + st->value_num = 1; + st->vr_type = IR_VR_TYPE_64; + struct ir_value val; + val.type = IR_VALUE_STACK_PTR; + st->addr_val.value = val; + st->addr_val.offset = -off * 8; + struct ir_insn_cg_extra *extra = insn_cg(st); + extra->dst = NULL; + + struct ir_basic_block **pos2; + array_for(pos2, fun->end_bbs) + { + struct ir_basic_block *bb = *pos2; + struct ir_insn *ld = create_insn_base_cg(bb); + insert_at_bb(ld, bb, INSERT_BACK_BEFORE_JMP); + ld->op = IR_INSN_LOADRAW; + ld->value_num = 0; + ld->vr_type = IR_VR_TYPE_64; + struct ir_value val; + val.type = IR_VALUE_STACK_PTR; + ld->addr_val.value = val; + ld->addr_val.offset = -off * 8; + + extra = insn_cg(ld); + extra->dst = fun->cg_info.regs[i]; + } + } + } +} + +static int check_need_spill(struct ir_function *fun) +{ + // Check if all instruction values are OK for translating + int res = 0; + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + struct ir_value *v0 = &insn->values[0]; + struct ir_value *v1 = &insn->values[1]; + enum val_type t0 = insn->value_num >= 1 ? vtype(*v0) : + UNDEF; + enum val_type t1 = insn->value_num >= 2 ? vtype(*v1) : + UNDEF; + enum val_type tdst = vtype_insn(insn); + struct ir_insn_cg_extra *extra = insn_cg(insn); + struct ir_insn *dst_insn = insn_dst(insn); + if (insn->op == IR_INSN_ALLOC) { + // dst = alloc + // Nothing to do + } else if (insn->op == IR_INSN_STORE) { + // store v0(dst) v1 + // Eequivalent to `v0 = v1` + insn->op = IR_INSN_ASSIGN; + DBGASSERT( + v0->type == + IR_VALUE_INSN); // Should be guaranteed by prog_check + DBGASSERT(v0->data.insn_d->op == IR_INSN_ALLOC); + insn->vr_type = v0->data.insn_d->vr_type; + extra->dst = v0->data.insn_d; + insn->value_num = 1; + *v0 = *v1; + res = 1; + } else if (insn->op == IR_INSN_LOAD) { + // stack = load stack + // stack = load reg + // reg = load reg + // reg = load stack + insn->op = IR_INSN_ASSIGN; + DBGASSERT( + v0->type == + IR_VALUE_INSN); // Should be guaranteed by prog_check + DBGASSERT(v0->data.insn_d->op == IR_INSN_ALLOC); + insn->vr_type = v0->data.insn_d->vr_type; + res = 1; + } else if (insn->op == IR_INSN_LOADRAW) { + // Load from memory + // reg = loadraw reg ==> OK + // reg = loadraw const ==> OK + + // stack = loadraw addr + // ==> + // R0 = loadraw addr + // stack = R0 + + // stack = loadraw stack + // ==> + // R0 = stack + // R0 = loadraw R0 + // stack = R0 + if (tdst == STACK) { + extra->dst = fun->cg_info.regs[0]; + struct ir_insn *tmp = + create_assign_insn_cg( + insn, + bpf_ir_value_insn( + fun->cg_info + .regs[0]), + INSERT_BACK); + insn_cg(tmp)->dst = dst_insn; + tmp->vr_type = insn->vr_type; + res = 1; + } + if (vtype(insn->addr_val.value) == STACK) { + // Question: are all memory address 64 bits? + load_stack_to_r0(fun, insn, + &insn->addr_val.value, + IR_VR_TYPE_64); + res = 1; + } + } else if (insn->op == IR_INSN_STORERAW) { + // Store some value to memory + // store ptr reg ==> OK + // store ptr stack + + // store stackptr stack + // ==> TODO! + if (t0 == STACK && + vtype(insn->addr_val.value) == STACK) { + CRITICAL("TODO!"); + } + if (t0 == CONST && + insn->vr_type == IR_VR_TYPE_64) { + CRITICAL("Not supported"); + } + // Question: are all memory address 64 bits? + if (t0 == STACK) { + load_stack_to_r0(fun, insn, v0, + IR_VR_TYPE_64); + res = 1; + } + if (vtype(insn->addr_val.value) == CONST) { + CRITICAL("Not Supported"); + } + if (vtype(insn->addr_val.value) == STACK) { + load_stack_to_r0(fun, insn, + &insn->addr_val.value, + IR_VR_TYPE_64); + res = 1; + } + } else if (is_alu(insn)) { + // Binary ALU + // reg = add reg reg + // reg = add reg const + // There should be NO stack + if (tdst == STACK) { + // stack = add ? ? + // ==> + // R0 = add ? ? + // stack = R0 + extra->dst = fun->cg_info.regs[0]; + struct ir_insn *tmp = + create_assign_insn_cg( + insn, + bpf_ir_value_insn( + fun->cg_info + .regs[0]), + INSERT_BACK); + tmp->vr_type = + alu_to_vr_type(insn->alu); + insn_cg(tmp)->dst = dst_insn; + res = 1; + } else { + if ((t0 != REG && t1 == REG) || + (t0 == CONST && t1 == STACK)) { + // reg = add !reg reg + // ==> + // reg = add reg !reg + struct ir_value tmp = *v0; + *v0 = *v1; + *v1 = tmp; + enum val_type ttmp = t0; + t0 = t1; + t1 = ttmp; + // No need to spill here + } + if (t0 == REG) { + // reg = add reg reg ==> OK + // reg = add reg const ==> OK + + // reg1 = add reg2 stack + // ==> + // If reg1 != reg2, + // reg1 = stack + // reg1 = add reg2 reg1 + // Else + // Choose reg3 != reg1, + // reg3 = stack + // reg1 = add reg1 reg3 + if (t1 == STACK) { + __u8 reg1 = + insn_cg(dst_insn) + ->alloc_reg; + __u8 reg2 = + insn_cg(v0->data.insn_d) + ->alloc_reg; + struct ir_insn *new_insn = + create_assign_insn_cg( + insn, + *v1, + INSERT_FRONT); + new_insn->vr_type = + alu_to_vr_type( + insn->alu); + v1->type = + IR_VALUE_INSN; + if (reg1 == reg2) { + __u8 reg = + reg1 == 0 ? + 1 : + 0; + insn_cg(new_insn) + ->dst = + fun->cg_info + .regs[reg]; + v1->data.insn_d = + fun->cg_info + .regs[reg]; + } else { + insn_cg(new_insn) + ->dst = + fun->cg_info + .regs[reg1]; + v1->data.insn_d = + fun->cg_info + .regs[reg1]; + } + res = 1; + } + } else { + // reg = add const const ==> OK + // reg = add c1 c2 + // ==> + // reg = c1 + // reg = add reg c2 + // OK + + // reg = add stack stack + if (t0 == STACK && + t1 == STACK) { + // reg1 = add stack1 stack2 + // ==> + // Found reg2 != reg1 + // reg1 = stack1 + // reg1 = add reg1 stack2 + __u8 reg1 = + insn_cg(dst_insn) + ->alloc_reg; + struct ir_insn *new_insn = + create_assign_insn_cg( + insn, + *v0, + INSERT_FRONT); + new_insn->vr_type = + alu_to_vr_type( + insn->alu); + insn_cg(new_insn)->dst = + fun->cg_info.regs + [reg1]; + v0->type = + IR_VALUE_INSN; + v0->data.insn_d = + fun->cg_info.regs + [reg1]; + res = 1; + } + // reg = add stack const ==> OK + // ==> + // reg = stack + // reg = add reg const + } + } + } else if (insn->op == IR_INSN_ASSIGN) { + // stack = reg (sized) + // stack = const (sized) + // reg = const (alu) + // reg = stack (sized) + // reg = reg + if (tdst == STACK && t0 == STACK) { + // Both stack positions are managed by us + load_stack_to_r0(fun, insn, v0, + IR_VR_TYPE_64); + res = 1; + } + if (tdst == STACK && t0 == CONST) { + if (insn->vr_type == IR_VR_TYPE_64) { + // First load to R0 + struct ir_insn *new_insn = + create_assign_insn_cg( + insn, *v0, + INSERT_FRONT); + new_insn->vr_type = + insn->vr_type; + insn_cg(new_insn)->dst = + fun->cg_info.regs[0]; + v0->type = IR_VALUE_INSN; + v0->data.insn_d = + fun->cg_info.regs[0]; + res = 1; + } + } + } else if (insn->op == IR_INSN_RET) { + // ret const/reg + // Done in explicit_reg pass + DBGASSERT(insn->value_num == 0); + } else if (insn->op == IR_INSN_CALL) { + // call() + // Should have no arguments + DBGASSERT(insn->value_num == 0); + } else if (insn->op == IR_INSN_JA) { + // OK + } else if (is_cond_jmp(insn)) { + // jmp reg const/reg + __u8 switched = 0; + if ((t0 != REG && t1 == REG) || + (t0 == CONST && t1 == STACK)) { + switched = 1; + struct ir_value tmp = *v0; + *v0 = *v1; + *v1 = tmp; + enum val_type ttmp = t0; + t0 = t1; + t1 = ttmp; + // No need to spill here + } + + if (t0 == REG) { + // jmp reg reg ==> OK + // jmp reg const ==> OK + // jmp reg stack + // ==> + // reg2 = stack + // jmp reg reg2 + if (t1 == STACK) { + __u8 reg1 = + insn_cg(v0->data.insn_d) + ->alloc_reg; + __u8 reg2 = reg1 == 0 ? 1 : 0; + struct ir_insn *new_insn = + create_assign_insn_cg( + insn, *v1, + INSERT_FRONT); + new_insn->vr_type = + alu_to_vr_type( + insn->alu); + insn_cg(new_insn)->dst = + fun->cg_info.regs[reg2]; + v1->type = IR_VALUE_INSN; + v1->data.insn_d = + fun->cg_info.regs[reg2]; + res = 1; + } + } else { + // jmp const1 const2 + // ==> + // %tmp = const1 + // jmp %tmp const2 + if (t0 == CONST && t1 == CONST) { + struct ir_insn *new_insn = + create_assign_insn_cg( + insn, *v0, + INSERT_FRONT); + new_insn->vr_type = + alu_to_vr_type( + insn->alu); + v0->type = IR_VALUE_INSN; + v0->data.insn_d = new_insn; + res = 1; + } + // jmp stack const + if (t0 == STACK && t1 == CONST) { + load_stack_to_r0( + fun, insn, v0, + alu_to_vr_type( + insn->alu)); + res = 1; + } + // jmp stack1 stack2 + // ==> + // R0 = stack1 + // R1 = stack2 + // jmp R0 R1 + if (t0 == STACK && t1 == STACK) { + load_stack_to_r0( + fun, insn, v0, + alu_to_vr_type( + insn->alu)); + res = 1; + } + } + if (switched) { + // Switch back + struct ir_value tmp = *v0; + *v0 = *v1; + *v1 = tmp; + } + } else { + CRITICAL("No such instruction"); + } + } + } + return res; +} + +static void calc_callee_num(struct ir_function *fun) +{ + __u8 reg_used[MAX_BPF_REG] = { 0 }; + + struct ir_insn **pos; + array_for(pos, fun->cg_info.all_var) + { + struct ir_insn_cg_extra *extra = insn_cg(*pos); + reg_used[extra->alloc_reg] = 1; + } + size_t off = 0; + for (__u8 i = BPF_REG_6; i < BPF_REG_10; ++i) { + if (reg_used[i]) { + off++; + } + } + fun->cg_info.callee_num = off; +} + +static void calc_stack_size(struct ir_function *fun) +{ + // Check callee + size_t off = 0; + if (fun->cg_info.spill_callee) { + off += fun->cg_info.callee_num * 8; + } + // Check all VR + size_t max = 0; + struct ir_insn **pos; + array_for(pos, fun->cg_info.all_var) + { + struct ir_insn_cg_extra *extra = insn_cg(*pos); + if (extra->spilled > 0) { + // Spilled! + if (extra->spilled > max) { + max = extra->spilled; + } + } + } + fun->cg_info.stack_offset = -(off + max * 8); + PRINT_LOG("Stack size: %d\n", fun->cg_info.stack_offset); +} + +static void add_stack_offset_pre_cg(struct ir_function *fun) +{ + // Pre CG + struct array users = fun->sp_users; + struct ir_insn **pos; + array_for(pos, users) + { + struct ir_insn *insn = *pos; + + if (insn->op == IR_INSN_LOADRAW || + insn->op == IR_INSN_STORERAW) { + // Also need to check if the value points to an INSN or a STACKPTR + // insn->addr_val.offset += offset; + continue; + } + struct array value_uses = get_operands(insn); + struct ir_value **pos2; + array_for(pos2, value_uses) + { + struct ir_value *val = *pos2; + if (val->type == IR_VALUE_STACK_PTR) { + // Stack pointer as value + struct ir_value new_val; + new_val.type = IR_VALUE_CONSTANT_RAWOFF; + struct ir_insn *new_insn = create_bin_insn( + insn, *val, new_val, IR_INSN_ADD, + IR_ALU_32, INSERT_FRONT); + new_val.type = IR_VALUE_INSN; + new_val.data.insn_d = new_insn; + *val = new_val; + } + } + bpf_ir_array_free(&value_uses); + } +} + +static void add_stack_offset(struct ir_function *fun, __s16 offset) +{ + struct array users = fun->sp_users; + struct ir_insn **pos; + array_for(pos, users) + { + struct ir_insn *insn = *pos; + + if (insn->op == IR_INSN_LOADRAW || + insn->op == IR_INSN_STORERAW) { + if (insn->addr_val.value.type == IR_VALUE_STACK_PTR) { + insn->addr_val.offset += offset; + continue; + } + } + struct array value_uses = get_operands(insn); + struct ir_value **pos2; + array_for(pos2, value_uses) + { + struct ir_value *val = *pos2; + DBGASSERT(val->type != IR_VALUE_STACK_PTR); + if (val->type == IR_VALUE_CONSTANT_RAWOFF) { + // Stack pointer as value + val->data.constant_d = offset; + } + } + bpf_ir_array_free(&value_uses); + } +} + +static struct pre_ir_insn load_reg_to_reg(__u8 dst, __u8 src) +{ + // MOV dst src + struct pre_ir_insn insn; + insn.opcode = BPF_MOV | BPF_X | BPF_ALU64; + insn.dst_reg = dst; + insn.src_reg = src; + return insn; +} + +static struct pre_ir_insn load_const_to_reg(__u8 dst, __s64 data, + enum ir_alu_type type) +{ + // MOV dst imm + struct pre_ir_insn insn; + insn.dst_reg = dst; + if (type == IR_ALU_64) { + insn.it = IMM64; + insn.imm64 = data; + insn.opcode = BPF_MOV | BPF_K | BPF_ALU64; + } else { + insn.it = IMM; + insn.imm = data; + insn.opcode = BPF_MOV | BPF_K | BPF_ALU; + } + return insn; +} + +static int vr_type_to_size(enum ir_vr_type type) +{ + switch (type) { + case IR_VR_TYPE_32: + return BPF_W; + case IR_VR_TYPE_16: + return BPF_H; + case IR_VR_TYPE_8: + return BPF_B; + case IR_VR_TYPE_64: + return BPF_DW; + default: + CRITICAL("Error"); + } +} + +static struct pre_ir_insn +load_addr_to_reg(__u8 dst, struct ir_address_value addr, enum ir_vr_type type) +{ + // MOV dst src + struct pre_ir_insn insn; + insn.dst_reg = dst; + insn.off = addr.offset; + int size = vr_type_to_size(type); + if (addr.value.type == IR_VALUE_STACK_PTR) { + insn.src_reg = BPF_REG_10; + insn.opcode = BPF_LDX | size | BPF_MEM; + } else if (addr.value.type == IR_VALUE_INSN) { + // Must be REG + DBGASSERT(vtype(addr.value) == REG); + // Load reg (addr) to reg + insn.src_reg = insn_cg(addr.value.data.insn_d)->alloc_reg; + insn.opcode = BPF_LDX | size | BPF_MEM; + } else if (addr.value.type == IR_VALUE_CONSTANT) { + // Must be U64 + insn.it = IMM64; + insn.imm64 = addr.value.data.constant_d; + insn.opcode = BPF_IMM | size | BPF_LD; + } else { + CRITICAL("Error"); + } + return insn; +} + +static struct pre_ir_insn store_reg_to_reg_mem(__u8 dst, __u8 src, __s16 offset, + enum ir_vr_type type) +{ + struct pre_ir_insn insn; + int size = vr_type_to_size(type); + insn.src_reg = src; + insn.off = offset; + insn.opcode = BPF_STX | size | BPF_MEM; + insn.dst_reg = dst; + return insn; +} + +static struct pre_ir_insn +store_const_to_reg_mem(__u8 dst, __s64 val, __s16 offset, enum ir_vr_type type) +{ + struct pre_ir_insn insn; + int size = vr_type_to_size(type); + insn.it = IMM; + insn.imm = val; + insn.off = offset; + insn.opcode = BPF_ST | size | BPF_MEM; + insn.dst_reg = dst; + return insn; +} + +static int alu_code(enum ir_insn_type insn) +{ + switch (insn) { + case IR_INSN_ADD: + return BPF_ADD; + case IR_INSN_SUB: + return BPF_SUB; + case IR_INSN_MUL: + return BPF_MUL; + case IR_INSN_MOD: + return BPF_MOD; + case IR_INSN_LSH: + return BPF_LSH; + default: + CRITICAL("Error"); + } +} + +static int jmp_code(enum ir_insn_type insn) +{ + switch (insn) { + case IR_INSN_JA: + return BPF_JA; + case IR_INSN_JEQ: + return BPF_JEQ; + case IR_INSN_JNE: + return BPF_JNE; + case IR_INSN_JLT: + return BPF_JLT; + case IR_INSN_JLE: + return BPF_JLE; + case IR_INSN_JGT: + return BPF_JGT; + case IR_INSN_JGE: + return BPF_JGE; + default: + CRITICAL("Error"); + } +} + +static struct pre_ir_insn alu_reg(__u8 dst, __u8 src, enum ir_alu_type type, + int opcode) +{ + struct pre_ir_insn insn; + insn.dst_reg = dst; + insn.src_reg = src; + int alu_class = type == IR_ALU_64 ? BPF_ALU64 : BPF_ALU; + insn.opcode = opcode | BPF_X | alu_class; + return insn; +} + +static struct pre_ir_insn alu_imm(__u8 dst, __s64 src, enum ir_alu_type type, + int opcode) +{ + struct pre_ir_insn insn; + insn.dst_reg = dst; + insn.src_reg = src; + int alu_class = type == IR_ALU_64 ? BPF_ALU64 : BPF_ALU; + if (type == IR_ALU_64) { + insn.it = IMM64; + insn.imm64 = src; + } else { + insn.it = IMM; + insn.imm = src; + } + insn.opcode = opcode | BPF_K | alu_class; + return insn; +} + +static struct pre_ir_insn cond_jmp_reg(__u8 dst, __u8 src, + enum ir_alu_type type, int opcode) +{ + struct pre_ir_insn insn; + insn.dst_reg = dst; + insn.src_reg = src; + int alu_class = type == IR_ALU_64 ? BPF_JMP : BPF_JMP32; + insn.opcode = opcode | alu_class | BPF_X; + return insn; +} + +static struct pre_ir_insn cond_jmp_imm(__u8 dst, __s64 src, + enum ir_alu_type type, int opcode) +{ + struct pre_ir_insn insn; + insn.dst_reg = dst; + insn.src_reg = src; + int alu_class = type == IR_ALU_64 ? BPF_JMP : BPF_JMP32; + if (type == IR_ALU_64) { + insn.it = IMM64; + insn.imm64 = src; + } else { + insn.it = IMM; + insn.imm = src; + } + insn.opcode = opcode | alu_class | BPF_K; + return insn; +} + +static __u8 get_alloc_reg(struct ir_insn *insn) +{ + return insn_cg(insn)->alloc_reg; +} + +static void translate(struct ir_function *fun) +{ + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + struct ir_insn *insn; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + struct ir_value v0 = insn->values[0]; + struct ir_value v1 = insn->values[1]; + enum val_type t0 = insn->value_num >= 1 ? vtype(v0) : + UNDEF; + enum val_type t1 = insn->value_num >= 2 ? vtype(v1) : + UNDEF; + enum val_type tdst = vtype_insn(insn); + struct ir_insn_cg_extra *extra = insn_cg(insn); + struct ir_insn *dst_insn = insn_dst(insn); + extra->translated_num = 1; // Default: 1 instruction + if (insn->op == IR_INSN_ALLOC) { + // Nothing to do + extra->translated_num = 0; + } else if (insn->op == IR_INSN_STORE) { + CRITICAL("Error"); + } else if (insn->op == IR_INSN_LOAD) { + CRITICAL("Error"); + } else if (insn->op == IR_INSN_LOADRAW) { + DBGASSERT(tdst == REG); + extra->translated[0] = load_addr_to_reg( + get_alloc_reg(dst_insn), insn->addr_val, + insn->vr_type); + } else if (insn->op == IR_INSN_STORERAW) { + // storeraw + if (insn->addr_val.value.type == + IR_VALUE_STACK_PTR) { + // Store value in the stack + if (t0 == REG) { + extra->translated + [0] = store_reg_to_reg_mem( + BPF_REG_10, + get_alloc_reg( + v0.data.insn_d), + insn->addr_val.offset, + insn->vr_type); + } else if (t0 == CONST) { + extra->translated[0] = + store_const_to_reg_mem( + BPF_REG_10, + v0.data.constant_d, + insn->addr_val + .offset, + insn->vr_type); + } else { + CRITICAL("Error"); + } + } else if (insn->addr_val.value.type == + IR_VALUE_INSN) { + // Store value in (address in the value) + DBGASSERT(vtype(insn->addr_val.value) == + REG); + // Store value in the stack + if (t0 == REG) { + extra->translated + [0] = store_reg_to_reg_mem( + get_alloc_reg( + insn->addr_val + .value + .data + .insn_d), + get_alloc_reg( + v0.data.insn_d), + insn->addr_val.offset, + insn->vr_type); + } else if (t0 == CONST) { + extra->translated + [0] = store_const_to_reg_mem( + get_alloc_reg( + insn->addr_val + .value + .data + .insn_d), + v0.data.constant_d, + insn->addr_val.offset, + insn->vr_type); + } else { + CRITICAL("Error"); + } + } else { + CRITICAL("Error"); + } + } else if (insn->op >= IR_INSN_ADD && + insn->op < IR_INSN_CALL) { + DBGASSERT(tdst == REG); + DBGASSERT(t0 == REG); + DBGASSERT(get_alloc_reg(dst_insn) == + get_alloc_reg(v0.data.insn_d)); + if (t1 == REG) { + extra->translated[0] = alu_reg( + get_alloc_reg(dst_insn), + get_alloc_reg(v1.data.insn_d), + insn->alu, alu_code(insn->op)); + } else if (t1 == CONST) { + extra->translated[0] = alu_imm( + get_alloc_reg(dst_insn), + v1.data.constant_d, insn->alu, + alu_code(insn->op)); + } else { + CRITICAL("Error"); + } + } else if (insn->op == IR_INSN_ASSIGN) { + // reg = const (alu) + // reg = reg + if (tdst == REG && t0 == CONST) { + extra->translated[0] = + load_const_to_reg( + get_alloc_reg(dst_insn), + v0.data.constant_d, + insn->alu); + } else if (tdst == REG && t0 == REG) { + extra->translated[0] = load_reg_to_reg( + get_alloc_reg(dst_insn), + get_alloc_reg(v0.data.insn_d)); + } else { + CRITICAL("Error"); + } + } else if (insn->op == IR_INSN_RET) { + extra->translated[0].opcode = BPF_EXIT | + BPF_JMP; + } else if (insn->op == IR_INSN_CALL) { + // Currently only support local helper functions + extra->translated[0].opcode = BPF_CALL | + BPF_JMP; + extra->translated[0].it = IMM; + extra->translated[0].imm = insn->fid; + } else if (insn->op == IR_INSN_JA) { + extra->translated[0].opcode = BPF_JMP | BPF_JA; + } else if (insn->op >= IR_INSN_JEQ && + insn->op < IR_INSN_PHI) { + DBGASSERT(t0 == REG || t1 == REG); + if (t0 == REG) { + if (t1 == REG) { + extra->translated + [0] = cond_jmp_reg( + get_alloc_reg( + v0.data.insn_d), + get_alloc_reg( + v1.data.insn_d), + insn->alu, + jmp_code(insn->op)); + } else if (t1 == CONST) { + extra->translated + [0] = cond_jmp_imm( + get_alloc_reg( + v0.data.insn_d), + v1.data.constant_d, + insn->alu, + jmp_code(insn->op)); + } else { + CRITICAL("Error"); + } + } else { + DBGASSERT(t0 == CONST); + DBGASSERT(t1 == REG); + extra->translated[0] = cond_jmp_imm( + get_alloc_reg(v1.data.insn_d), + v0.data.constant_d, insn->alu, + jmp_code(insn->op)); + } + } else { + CRITICAL("No such instruction"); + } + } + } +} + +// Interface Implementation + +int bpf_ir_code_gen(struct ir_function *fun) +{ + // Preparation + + // Step 1: Flag all raw stack access + add_stack_offset_pre_cg(fun); + bpf_ir_prog_check(fun); + + // Step 2: Eliminate SSA + to_cssa(fun); + bpf_ir_prog_check(fun); + + print_ir_prog_pre_cg(fun, "To CSSA"); + + // Init CG, start real code generation + init_cg(fun); + + // Debugging settings + fun->cg_info.spill_callee = 0; + + // Step 3: Use explicit real registers + explicit_reg(fun); // Still in SSA form, users are available + print_ir_prog_cg_dst(fun, "Explicit REG"); + + // Step 4: SSA Destruction + // users not available from now on + remove_phi(fun); + print_ir_prog_cg_dst(fun, "PHI Removal"); + + // print_ir_prog_reachable(fun); + + int need_spill = 1; + int iterations = 0; + + while (need_spill) { + iterations++; + // Step 5: Liveness Analysis + liveness_analysis(fun); + + // Step 6: Conflict Analysis + conflict_analysis(fun); + PRINT_LOG("Conflicting graph:\n"); + bpf_ir_print_interference_graph(fun); + + // Step 7: Graph coloring + graph_coloring(fun); + coaleasing(fun); + PRINT_LOG("Conflicting graph (after coloring):\n"); + bpf_ir_print_interference_graph(fun); + print_ir_prog_cg_alloc(fun, "After RA"); + + // Step 8: Check if need to spill and spill + need_spill = check_need_spill(fun); + // print_ir_prog_cg_dst(fun, "After Spilling"); + if (need_spill) { + // Still need to spill + PRINT_LOG("Need to spill...\n"); + clean_cg(fun); + } + } + + // Register allocation finished (All registers are fixed) + PRINT_LOG("Register allocation finished in %d iteratinos\n", + iterations); + print_ir_prog_cg_alloc(fun, "After RA & Spilling"); + + // Step 9: Calculate stack size + if (fun->cg_info.spill_callee) { + calc_callee_num(fun); + } + calc_stack_size(fun); + + // Step 10: Shift raw stack operations + add_stack_offset(fun, fun->cg_info.stack_offset); + print_ir_prog_cg_alloc(fun, "Shifting stack access"); + + // Step 11: Spill callee saved registers + if (fun->cg_info.spill_callee) { + spill_callee(fun); + print_ir_prog_cg_alloc(fun, "Spilling callee-saved regs"); + } + + // Step 12: Normalize + normalize(fun); + print_ir_prog_cg_alloc(fun, "Normalization"); + + // Step 13: Direct Translation + translate(fun); + + // Step 14: Relocation + relocate(fun); + + // Step 15: Synthesize + synthesize(fun); + + // Free CG resources + free_cg_res(fun); + return 0; +} - // Free CG resources - free_cg_res(fun); +int bpf_ir_init_insn_cg(struct ir_insn *insn) +{ + struct ir_insn_cg_extra *extra = NULL; + SAFE_MALLOC(extra, sizeof(struct ir_insn_cg_extra)); + // When init, the destination is itself + if (is_void(insn)) { + extra->dst = NULL; + } else { + extra->dst = insn; + } + INIT_ARRAY(&extra->adj, struct ir_insn *); + extra->allocated = 0; + extra->spilled = 0; + extra->alloc_reg = 0; + INIT_ARRAY(&extra->gen, struct ir_insn *); + INIT_ARRAY(&extra->kill, struct ir_insn *); + INIT_ARRAY(&extra->in, struct ir_insn *); + INIT_ARRAY(&extra->out, struct ir_insn *); + extra->translated_num = 0; + insn->user_data = extra; + return 0; } diff --git a/IR/ir_helper.c b/IR/ir_helper.c index a615e4f5..353cc403 100644 --- a/IR/ir_helper.c +++ b/IR/ir_helper.c @@ -1,492 +1,569 @@ -#include -#include -#include "array.h" -#include "bpf_ir.h" -#include "code_gen.h" -#include "dbg.h" -#include "ir_insn.h" -#include "list.h" -#include "ir_fun.h" +#include + +int bpf_ir_valid_alu_type(enum ir_alu_type type) +{ + return type >= IR_ALU_32 && type <= IR_ALU_64; +} + +int bpf_ir_valid_vr_type(enum ir_vr_type type) +{ + return type >= IR_VR_TYPE_8 && type <= IR_VR_TYPE_64; +} /// Reset visited flag -void clean_env_all(struct ir_function *fun) { - for (size_t i = 0; i < fun->all_bbs.num_elem; ++i) { - struct ir_basic_block *bb = ((struct ir_basic_block **)(fun->all_bbs.data))[i]; - bb->_visited = 0; - bb->user_data = NULL; - struct list_head *p = NULL; - list_for_each(p, &bb->ir_insn_head) { - struct ir_insn *insn = list_entry(p, struct ir_insn, list_ptr); - insn->user_data = NULL; - insn->_visited = 0; - } - } -} - -void clean_env(struct ir_function *fun) { - for (size_t i = 0; i < fun->all_bbs.num_elem; ++i) { - struct ir_basic_block *bb = ((struct ir_basic_block **)(fun->all_bbs.data))[i]; - bb->_visited = 0; - struct list_head *p = NULL; - list_for_each(p, &bb->ir_insn_head) { - struct ir_insn *insn = list_entry(p, struct ir_insn, list_ptr); - insn->_visited = 0; - } - } +void clean_env_all(struct ir_function *fun) +{ + for (size_t i = 0; i < fun->all_bbs.num_elem; ++i) { + struct ir_basic_block *bb = + ((struct ir_basic_block **)(fun->all_bbs.data))[i]; + bb->_visited = 0; + bb->user_data = NULL; + struct list_head *p = NULL; + list_for_each(p, &bb->ir_insn_head) { + struct ir_insn *insn = + list_entry(p, struct ir_insn, list_ptr); + insn->user_data = NULL; + insn->_visited = 0; + } + } +} + +void clean_env(struct ir_function *fun) +{ + for (size_t i = 0; i < fun->all_bbs.num_elem; ++i) { + struct ir_basic_block *bb = + ((struct ir_basic_block **)(fun->all_bbs.data))[i]; + bb->_visited = 0; + struct list_head *p = NULL; + list_for_each(p, &bb->ir_insn_head) { + struct ir_insn *insn = + list_entry(p, struct ir_insn, list_ptr); + insn->_visited = 0; + } + } } /// Reset instruction/BB ID -void clean_tag(struct ir_function *fun) { - for (size_t i = 0; i < fun->all_bbs.num_elem; ++i) { - struct ir_basic_block *ir_bb = ((struct ir_basic_block **)(fun->all_bbs.data))[i]; - ir_bb->_id = -1; - struct list_head *p = NULL; - list_for_each(p, &ir_bb->ir_insn_head) { - struct ir_insn *insn = list_entry(p, struct ir_insn, list_ptr); - insn->_insn_id = -1; - } - } -} - -void print_constant(struct ir_constant d) { - switch (d.type) { - case IR_CONSTANT_S32: - if (d.data.s32_d < 0) { - printf("-0x%x", -d.data.s32_d); - } else { - printf("0x%x", d.data.s32_d); - } - break; - case IR_CONSTANT_U32: - printf("0x%x", d.data.u32_d); - break; - case IR_CONSTANT_U64: - printf("0x%llx", d.data.u64_d); - break; - case IR_CONSTANT_S64: - if (d.data.s64_d < 0) { - printf("-0x%llx", -d.data.s64_d); - } else { - printf("0x%llx", d.data.s64_d); - } - break; - case IR_CONSTANT_S16: - if (d.data.s16_d < 0) { - printf("-0x%x", -d.data.s16_d); - } else { - printf("0x%x", d.data.s16_d); - } - break; - case IR_CONSTANT_U16: - printf("0x%x", d.data.u16_d); - break; - default: - CRITICAL("Unknown constant type"); - } -} - -void print_insn_ptr_base(struct ir_insn *insn) { - if (insn->op == IR_INSN_REG) { - printf("R%u", insn_cg(insn)->alloc_reg); - return; - } - if (insn->op == IR_INSN_FUNCTIONARG) { - printf("arg%u", insn->fid); - return; - } - if (insn->_insn_id == SIZE_MAX) { - printf("%p", insn); - return; - } - printf("%%%zu", insn->_insn_id); -} - -void print_insn_ptr(struct ir_insn *insn, void (*print_ir)(struct ir_insn *)) { - if (print_ir) { - print_ir(insn); - } else { - print_insn_ptr_base(insn); - } -} - -void print_bb_ptr(struct ir_basic_block *insn) { - if (insn->_id == SIZE_MAX) { - printf("b%p", insn); - return; - } - printf("b%zu", insn->_id); -} - -void print_ir_value_full(struct ir_value v, void (*print_ir)(struct ir_insn *)) { - switch (v.type) { - case IR_VALUE_INSN: - print_insn_ptr(v.data.insn_d, print_ir); - break; - case IR_VALUE_STACK_PTR: - printf("SP"); - break; - case IR_VALUE_CONSTANT: - print_constant(v.data.constant_d); - break; - case IR_VALUE_UNDEF: - printf("undef"); - break; - default: - CRITICAL("Unknown IR value type"); - } -} - -void print_ir_value(struct ir_value v) { - print_ir_value_full(v, 0); -} - -void print_address_value_full(struct ir_address_value v, void (*print_ir)(struct ir_insn *)) { - print_ir_value_full(v.value, print_ir); - if (v.offset != 0) { - printf("+%d", v.offset); - } -} - -void print_address_value(struct ir_address_value v) { - print_address_value_full(v, 0); -} - -void print_vr_type(enum ir_vr_type t) { - switch (t) { - case IR_VR_TYPE_8: - printf("u8"); - break; - case IR_VR_TYPE_64: - printf("u64"); - break; - case IR_VR_TYPE_16: - printf("u16"); - break; - case IR_VR_TYPE_32: - printf("u32"); - break; - default: - CRITICAL("Unknown VR type"); - } -} - -void print_phi_full(struct array *phi, void (*print_ir)(struct ir_insn *)) { - for (size_t i = 0; i < phi->num_elem; ++i) { - struct phi_value v = ((struct phi_value *)(phi->data))[i]; - printf(" <"); - print_bb_ptr(v.bb); - printf(" -> "); - print_ir_value_full(v.value, print_ir); - printf(">"); - } -} - -void print_phi(struct array *phi) { - print_phi_full(phi, 0); +void clean_tag(struct ir_function *fun) +{ + for (size_t i = 0; i < fun->all_bbs.num_elem; ++i) { + struct ir_basic_block *ir_bb = + ((struct ir_basic_block **)(fun->all_bbs.data))[i]; + ir_bb->_id = -1; + struct list_head *p = NULL; + list_for_each(p, &ir_bb->ir_insn_head) { + struct ir_insn *insn = + list_entry(p, struct ir_insn, list_ptr); + insn->_insn_id = -1; + } + } +} + +void print_insn_ptr_base(struct ir_insn *insn) +{ + if (insn->op == IR_INSN_REG) { + PRINT_LOG("R%u", insn_cg(insn)->alloc_reg); + return; + } + if (insn->op == IR_INSN_FUNCTIONARG) { + PRINT_LOG("arg%u", insn->fid); + return; + } + if (insn->_insn_id == SIZET_MAX) { + PRINT_LOG("%p", insn); + return; + } + PRINT_LOG("%%%zu", insn->_insn_id); +} + +void print_insn_ptr(struct ir_insn *insn, void (*print_ir)(struct ir_insn *)) +{ + if (print_ir) { + print_ir(insn); + } else { + print_insn_ptr_base(insn); + } +} + +void print_bb_ptr(struct ir_basic_block *insn) +{ + if (insn->_id == SIZE_MAX) { + PRINT_LOG("b%p", insn); + return; + } + PRINT_LOG("b%zu", insn->_id); +} + +void print_ir_value_full(struct ir_value v, void (*print_ir)(struct ir_insn *)) +{ + switch (v.type) { + case IR_VALUE_INSN: + print_insn_ptr(v.data.insn_d, print_ir); + break; + case IR_VALUE_STACK_PTR: + PRINT_LOG("SP"); + break; + case IR_VALUE_CONSTANT: + PRINT_LOG("0x%llx", v.data.constant_d); + break; + case IR_VALUE_CONSTANT_RAWOFF: + PRINT_LOG("(hole)"); + break; + case IR_VALUE_UNDEF: + PRINT_LOG("undef"); + break; + default: + CRITICAL("Unknown IR value type"); + } +} + +void print_ir_value(struct ir_value v) +{ + print_ir_value_full(v, 0); +} + +void print_address_value_full(struct ir_address_value v, + void (*print_ir)(struct ir_insn *)) +{ + print_ir_value_full(v.value, print_ir); + if (v.offset != 0) { + PRINT_LOG("+%d", v.offset); + } +} + +void print_address_value(struct ir_address_value v) +{ + print_address_value_full(v, 0); +} + +void print_vr_type(enum ir_vr_type t) +{ + switch (t) { + case IR_VR_TYPE_8: + PRINT_LOG("u8"); + break; + case IR_VR_TYPE_64: + PRINT_LOG("u64"); + break; + case IR_VR_TYPE_16: + PRINT_LOG("u16"); + break; + case IR_VR_TYPE_32: + PRINT_LOG("u32"); + break; + default: + CRITICAL("Unknown VR type"); + } +} + +void print_phi_full(struct array *phi, void (*print_ir)(struct ir_insn *)) +{ + for (size_t i = 0; i < phi->num_elem; ++i) { + struct phi_value v = ((struct phi_value *)(phi->data))[i]; + PRINT_LOG(" <"); + print_bb_ptr(v.bb); + PRINT_LOG(" -> "); + print_ir_value_full(v.value, print_ir); + PRINT_LOG(">"); + } +} + +void print_phi(struct array *phi) +{ + print_phi_full(phi, 0); } /** Print the IR insn */ -void print_ir_insn_full(struct ir_insn *insn, void (*print_ir)(struct ir_insn *)) { - switch (insn->op) { - case IR_INSN_ALLOC: - printf("alloc "); - print_vr_type(insn->vr_type); - break; - case IR_INSN_STORE: - printf("store "); - print_ir_value_full(insn->values[0], print_ir); - printf(", "); - print_ir_value_full(insn->values[1], print_ir); - break; - case IR_INSN_LOAD: - printf("load "); - print_vr_type(insn->vr_type); - printf(", "); - print_ir_value_full(insn->values[0], print_ir); - break; - case IR_INSN_LOADRAW: - printf("loadraw "); - print_vr_type(insn->vr_type); - printf(" "); - print_address_value_full(insn->addr_val, print_ir); - break; - case IR_INSN_STORERAW: - printf("storeraw "); - print_vr_type(insn->vr_type); - printf(" "); - print_address_value_full(insn->addr_val, print_ir); - printf(" "); - print_ir_value_full(insn->values[0], print_ir); - break; - case IR_INSN_ADD: - printf("add "); - print_ir_value_full(insn->values[0], print_ir); - printf(", "); - print_ir_value_full(insn->values[1], print_ir); - break; - case IR_INSN_SUB: - printf("sub "); - print_ir_value_full(insn->values[0], print_ir); - printf(", "); - print_ir_value_full(insn->values[1], print_ir); - break; - case IR_INSN_MUL: - printf("mul "); - print_ir_value_full(insn->values[0], print_ir); - printf(", "); - print_ir_value_full(insn->values[1], print_ir); - break; - case IR_INSN_CALL: - printf("call __built_in_func_%d(", insn->fid); - if (insn->value_num >= 1) { - print_ir_value_full(insn->values[0], print_ir); - } - for (size_t i = 1; i < insn->value_num; ++i) { - printf(", "); - print_ir_value_full(insn->values[i], print_ir); - } - printf(")"); - break; - case IR_INSN_RET: - printf("ret "); - if (insn->value_num > 0) { - print_ir_value_full(insn->values[0], print_ir); - } - break; - case IR_INSN_JA: - printf("ja "); - print_bb_ptr(insn->bb1); - break; - case IR_INSN_JEQ: - printf("jeq "); - print_ir_value_full(insn->values[0], print_ir); - printf(", "); - print_ir_value_full(insn->values[1], print_ir); - printf(", "); - print_bb_ptr(insn->bb1); - printf("/"); - print_bb_ptr(insn->bb2); - break; - case IR_INSN_JGT: - printf("jgt "); - print_ir_value_full(insn->values[0], print_ir); - printf(", "); - print_ir_value_full(insn->values[1], print_ir); - printf(", "); - print_bb_ptr(insn->bb1); - printf("/"); - print_bb_ptr(insn->bb2); - break; - case IR_INSN_JGE: - printf("jge "); - print_ir_value_full(insn->values[0], print_ir); - printf(", "); - print_ir_value_full(insn->values[1], print_ir); - printf(", "); - print_bb_ptr(insn->bb1); - printf("/"); - print_bb_ptr(insn->bb2); - break; - case IR_INSN_JLT: - printf("jlt "); - print_ir_value_full(insn->values[0], print_ir); - printf(", "); - print_ir_value_full(insn->values[1], print_ir); - printf(", "); - print_bb_ptr(insn->bb1); - printf("/"); - print_bb_ptr(insn->bb2); - break; - case IR_INSN_JLE: - printf("jle "); - print_ir_value_full(insn->values[0], print_ir); - printf(", "); - print_ir_value_full(insn->values[1], print_ir); - printf(", "); - print_bb_ptr(insn->bb1); - printf("/"); - print_bb_ptr(insn->bb2); - break; - case IR_INSN_JNE: - printf("jne "); - print_ir_value_full(insn->values[0], print_ir); - printf(", "); - print_ir_value_full(insn->values[1], print_ir); - printf(", "); - print_bb_ptr(insn->bb1); - printf("/"); - print_bb_ptr(insn->bb2); - break; - case IR_INSN_PHI: - printf("phi"); - print_phi_full(&insn->phi, print_ir); - break; - case IR_INSN_LSH: - printf("lsh "); - print_ir_value_full(insn->values[0], print_ir); - printf(", "); - print_ir_value_full(insn->values[1], print_ir); - break; - case IR_INSN_MOD: - printf("mod "); - print_ir_value_full(insn->values[0], print_ir); - printf(", "); - print_ir_value_full(insn->values[1], print_ir); - break; - case IR_INSN_ASSIGN: - print_ir_value_full(insn->values[0], print_ir); - break; - default: - CRITICAL("Unknown IR insn"); - } -} - -void print_ir_insn(struct ir_insn *insn) { - print_ir_insn_full(insn, 0); -} - -void print_raw_ir_insn_full(struct ir_insn *insn, void (*print_ir)(struct ir_insn *)) { - if (print_ir) { - print_ir(insn); - } else { - printf("%p", insn); - } - printf(" = "); - print_ir_insn_full(insn, print_ir); - printf("\n"); -} - -void print_raw_ir_insn(struct ir_insn *insn) { - print_raw_ir_insn_full(insn, 0); -} - -void print_ir_bb(struct ir_basic_block *bb, void (*post_bb)(struct ir_basic_block *), - void (*post_insn)(struct ir_insn *), void (*print_insn_name)(struct ir_insn *)) { - if (bb->_visited) { - return; - } - bb->_visited = 1; - printf("b%zu:\n", bb->_id); - struct list_head *p = NULL; - list_for_each(p, &bb->ir_insn_head) { - struct ir_insn *insn = list_entry(p, struct ir_insn, list_ptr); - if (is_void(insn)) { - printf(" "); - } else { - printf(" "); - if (print_insn_name) { - print_insn_name(insn); - } else { - printf("%%%zu", insn->_insn_id); - } - printf(" = "); - } - - print_ir_insn_full(insn, print_insn_name); - printf("\n"); - if (post_insn) { - post_insn(insn); - } - } - if (post_bb) { - post_bb(bb); - } - for (size_t i = 0; i < bb->succs.num_elem; ++i) { - struct ir_basic_block *next = ((struct ir_basic_block **)(bb->succs.data))[i]; - print_ir_bb(next, post_bb, post_insn, print_insn_name); - } -} - -void print_raw_ir_bb_full(struct ir_basic_block *bb, void (*print_ir)(struct ir_insn *)) { - printf("b%p:\n", bb); - struct list_head *p = NULL; - list_for_each(p, &bb->ir_insn_head) { - struct ir_insn *insn = list_entry(p, struct ir_insn, list_ptr); - printf(" "); - print_raw_ir_insn_full(insn, print_ir); - } -} - -void print_raw_ir_bb(struct ir_basic_block *bb) { - print_raw_ir_bb_full(bb, 0); -} - -void assign_id(struct ir_basic_block *bb, size_t *cnt, size_t *bb_cnt) { - if (bb->_visited) { - return; - } - bb->_visited = 1; - bb->_id = (*bb_cnt)++; - struct list_head *p = NULL; - list_for_each(p, &bb->ir_insn_head) { - struct ir_insn *insn = list_entry(p, struct ir_insn, list_ptr); - if (!is_void(insn)) { - insn->_insn_id = (*cnt)++; - } - } - struct ir_basic_block **next; - array_for(next, bb->succs) { - assign_id(*next, cnt, bb_cnt); - } -} - -void tag_ir(struct ir_function *fun) { - size_t cnt = 0; - size_t bb_cnt = 0; - clean_env(fun); - assign_id(fun->entry, &cnt, &bb_cnt); - clean_env(fun); -} - -void print_bb_succ(struct ir_basic_block *bb) { - printf("succs: "); - struct ir_basic_block **next; - array_for(next, bb->succs) { - print_bb_ptr(*next); - printf(" "); - } - printf("\n\n"); -} - -void print_ir_prog(struct ir_function *fun) { - tag_ir(fun); - print_ir_bb(fun->entry, print_bb_succ, NULL, NULL); - clean_tag(fun); -} - -void print_ir_dst(struct ir_insn *insn) { - insn = dst(insn); - if (insn) { - print_insn_ptr_base(insn); - } else { - printf("(NULL)"); - } -} - -void print_ir_alloc(struct ir_insn *insn) { - insn = dst(insn); - if (insn) { - struct ir_insn_cg_extra *extra = insn_cg(insn); - if (extra->allocated) { - if (extra->spilled) { - printf("sp-%zu", extra->spilled * 8); - } else { - printf("r%u", extra->alloc_reg); - } - } else { - CRITICAL("Not allocated"); - } - } else { - printf("(NULL)"); - } -} - -void print_ir_prog_advanced(struct ir_function *fun, void (*post_bb)(struct ir_basic_block *), - void (*post_insn)(struct ir_insn *), - void (*print_insn_name)(struct ir_insn *)) { - tag_ir(fun); - print_ir_bb(fun->entry, post_bb, post_insn, print_insn_name); - clean_tag(fun); +void print_ir_insn_full(struct ir_insn *insn, + void (*print_ir)(struct ir_insn *)) +{ + switch (insn->op) { + case IR_INSN_ALLOC: + PRINT_LOG("alloc "); + print_vr_type(insn->vr_type); + break; + case IR_INSN_STORE: + PRINT_LOG("store "); + print_ir_value_full(insn->values[0], print_ir); + PRINT_LOG(", "); + print_ir_value_full(insn->values[1], print_ir); + break; + case IR_INSN_LOAD: + PRINT_LOG("load "); + print_ir_value_full(insn->values[0], print_ir); + break; + case IR_INSN_LOADRAW: + PRINT_LOG("loadraw "); + print_vr_type(insn->vr_type); + PRINT_LOG(" "); + print_address_value_full(insn->addr_val, print_ir); + break; + case IR_INSN_STORERAW: + PRINT_LOG("storeraw "); + print_vr_type(insn->vr_type); + PRINT_LOG(" "); + print_address_value_full(insn->addr_val, print_ir); + PRINT_LOG(" "); + print_ir_value_full(insn->values[0], print_ir); + break; + case IR_INSN_ADD: + PRINT_LOG("add "); + print_ir_value_full(insn->values[0], print_ir); + PRINT_LOG(", "); + print_ir_value_full(insn->values[1], print_ir); + break; + case IR_INSN_SUB: + PRINT_LOG("sub "); + print_ir_value_full(insn->values[0], print_ir); + PRINT_LOG(", "); + print_ir_value_full(insn->values[1], print_ir); + break; + case IR_INSN_MUL: + PRINT_LOG("mul "); + print_ir_value_full(insn->values[0], print_ir); + PRINT_LOG(", "); + print_ir_value_full(insn->values[1], print_ir); + break; + case IR_INSN_CALL: + PRINT_LOG("call __built_in_func_%d(", insn->fid); + if (insn->value_num >= 1) { + print_ir_value_full(insn->values[0], print_ir); + } + for (size_t i = 1; i < insn->value_num; ++i) { + PRINT_LOG(", "); + print_ir_value_full(insn->values[i], print_ir); + } + PRINT_LOG(")"); + break; + case IR_INSN_RET: + PRINT_LOG("ret "); + if (insn->value_num > 0) { + print_ir_value_full(insn->values[0], print_ir); + } + break; + case IR_INSN_JA: + PRINT_LOG("ja "); + print_bb_ptr(insn->bb1); + break; + case IR_INSN_JEQ: + PRINT_LOG("jeq "); + print_ir_value_full(insn->values[0], print_ir); + PRINT_LOG(", "); + print_ir_value_full(insn->values[1], print_ir); + PRINT_LOG(", "); + print_bb_ptr(insn->bb1); + PRINT_LOG("/"); + print_bb_ptr(insn->bb2); + break; + case IR_INSN_JGT: + PRINT_LOG("jgt "); + print_ir_value_full(insn->values[0], print_ir); + PRINT_LOG(", "); + print_ir_value_full(insn->values[1], print_ir); + PRINT_LOG(", "); + print_bb_ptr(insn->bb1); + PRINT_LOG("/"); + print_bb_ptr(insn->bb2); + break; + case IR_INSN_JGE: + PRINT_LOG("jge "); + print_ir_value_full(insn->values[0], print_ir); + PRINT_LOG(", "); + print_ir_value_full(insn->values[1], print_ir); + PRINT_LOG(", "); + print_bb_ptr(insn->bb1); + PRINT_LOG("/"); + print_bb_ptr(insn->bb2); + break; + case IR_INSN_JLT: + PRINT_LOG("jlt "); + print_ir_value_full(insn->values[0], print_ir); + PRINT_LOG(", "); + print_ir_value_full(insn->values[1], print_ir); + PRINT_LOG(", "); + print_bb_ptr(insn->bb1); + PRINT_LOG("/"); + print_bb_ptr(insn->bb2); + break; + case IR_INSN_JLE: + PRINT_LOG("jle "); + print_ir_value_full(insn->values[0], print_ir); + PRINT_LOG(", "); + print_ir_value_full(insn->values[1], print_ir); + PRINT_LOG(", "); + print_bb_ptr(insn->bb1); + PRINT_LOG("/"); + print_bb_ptr(insn->bb2); + break; + case IR_INSN_JNE: + PRINT_LOG("jne "); + print_ir_value_full(insn->values[0], print_ir); + PRINT_LOG(", "); + print_ir_value_full(insn->values[1], print_ir); + PRINT_LOG(", "); + print_bb_ptr(insn->bb1); + PRINT_LOG("/"); + print_bb_ptr(insn->bb2); + break; + case IR_INSN_PHI: + PRINT_LOG("phi"); + print_phi_full(&insn->phi, print_ir); + break; + case IR_INSN_LSH: + PRINT_LOG("lsh "); + print_ir_value_full(insn->values[0], print_ir); + PRINT_LOG(", "); + print_ir_value_full(insn->values[1], print_ir); + break; + case IR_INSN_MOD: + PRINT_LOG("mod "); + print_ir_value_full(insn->values[0], print_ir); + PRINT_LOG(", "); + print_ir_value_full(insn->values[1], print_ir); + break; + case IR_INSN_ASSIGN: + print_ir_value_full(insn->values[0], print_ir); + break; + default: + CRITICAL("Unknown IR insn"); + } +} + +void print_ir_insn(struct ir_insn *insn) +{ + print_ir_insn_full(insn, 0); +} + +void print_raw_ir_insn_full(struct ir_insn *insn, + void (*print_ir)(struct ir_insn *)) +{ + if (print_ir) { + print_ir(insn); + } else { + PRINT_LOG("%p", insn); + } + PRINT_LOG(" = "); + print_ir_insn_full(insn, print_ir); + PRINT_LOG("\n"); +} + +void print_raw_ir_insn(struct ir_insn *insn) +{ + print_raw_ir_insn_full(insn, 0); +} + +void print_ir_bb_no_rec(struct ir_basic_block *bb, + void (*post_bb)(struct ir_basic_block *), + void (*post_insn)(struct ir_insn *), + void (*print_insn_name)(struct ir_insn *)) +{ + PRINT_LOG("b%zu:\n", bb->_id); + struct list_head *p = NULL; + list_for_each(p, &bb->ir_insn_head) { + struct ir_insn *insn = list_entry(p, struct ir_insn, list_ptr); + if (is_void(insn)) { + PRINT_LOG(" "); + } else { + PRINT_LOG(" "); + if (print_insn_name) { + print_insn_name(insn); + } else { + PRINT_LOG("%%%zu", insn->_insn_id); + } + PRINT_LOG(" = "); + } + + print_ir_insn_full(insn, print_insn_name); + PRINT_LOG("\n"); + if (post_insn) { + post_insn(insn); + } + } + if (post_bb) { + post_bb(bb); + } +} + +void print_ir_bb(struct ir_basic_block *bb, + void (*post_bb)(struct ir_basic_block *), + void (*post_insn)(struct ir_insn *), + void (*print_insn_name)(struct ir_insn *)) +{ + if (bb->_visited) { + return; + } + bb->_visited = 1; + print_ir_bb_no_rec(bb, post_bb, post_insn, print_insn_name); + for (size_t i = 0; i < bb->succs.num_elem; ++i) { + struct ir_basic_block *next = + ((struct ir_basic_block **)(bb->succs.data))[i]; + print_ir_bb(next, post_bb, post_insn, print_insn_name); + } +} + +void print_ir_prog_reachable(struct ir_function *fun) +{ + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + print_ir_bb_no_rec(bb, NULL, NULL, NULL); + } +} + +void print_raw_ir_bb_full(struct ir_basic_block *bb, + void (*print_ir)(struct ir_insn *)) +{ + PRINT_LOG("b%p:\n", bb); + struct list_head *p = NULL; + list_for_each(p, &bb->ir_insn_head) { + struct ir_insn *insn = list_entry(p, struct ir_insn, list_ptr); + PRINT_LOG(" "); + print_raw_ir_insn_full(insn, print_ir); + } +} + +void print_raw_ir_bb(struct ir_basic_block *bb) +{ + print_raw_ir_bb_full(bb, 0); +} + +void assign_id(struct ir_basic_block *bb, size_t *cnt, size_t *bb_cnt) +{ + if (bb->_visited) { + return; + } + bb->_visited = 1; + bb->_id = (*bb_cnt)++; + struct list_head *p = NULL; + list_for_each(p, &bb->ir_insn_head) { + struct ir_insn *insn = list_entry(p, struct ir_insn, list_ptr); + if (!is_void(insn)) { + insn->_insn_id = (*cnt)++; + } + } + struct ir_basic_block **next; + array_for(next, bb->succs) + { + assign_id(*next, cnt, bb_cnt); + } +} + +void tag_ir(struct ir_function *fun) +{ + clean_tag(fun); + size_t cnt = 0; + size_t bb_cnt = 0; + clean_env(fun); + assign_id(fun->entry, &cnt, &bb_cnt); + clean_env(fun); +} + +void print_bb_succ(struct ir_basic_block *bb) +{ + PRINT_LOG("succs: "); + struct ir_basic_block **next; + array_for(next, bb->succs) + { + print_bb_ptr(*next); + PRINT_LOG(" "); + } + PRINT_LOG("\n\n"); +} + +void print_ir_prog(struct ir_function *fun) +{ + tag_ir(fun); + print_ir_bb(fun->entry, NULL, NULL, NULL); +} + +void print_ir_dst(struct ir_insn *insn) +{ + insn = insn_dst(insn); + if (insn) { + print_insn_ptr_base(insn); + } else { + PRINT_LOG("(NULL)"); + } +} + +void print_ir_alloc(struct ir_insn *insn) +{ + insn = insn_dst(insn); + if (insn) { + struct ir_insn_cg_extra *extra = insn_cg(insn); + if (extra->allocated) { + if (extra->spilled) { + PRINT_LOG("sp-%zu", extra->spilled * 8); + } else { + PRINT_LOG("r%u", extra->alloc_reg); + } + } else { + CRITICAL("Not allocated"); + } + } else { + PRINT_LOG("(NULL)"); + } +} + +void print_ir_prog_advanced(struct ir_function *fun, + void (*post_bb)(struct ir_basic_block *), + void (*post_insn)(struct ir_insn *), + void (*print_insn_name)(struct ir_insn *)) +{ + tag_ir(fun); + print_ir_bb(fun->entry, post_bb, post_insn, print_insn_name); +} + +void print_ir_insn_err(struct ir_insn *insn, char *msg) +{ + PRINT_LOG("In BB %zu,\n", insn->parent_bb->_id); + struct ir_insn *prev = prev_insn(insn); + struct ir_insn *next = next_insn(insn); + if (prev) { + PRINT_LOG(" "); + if (!is_void(prev)) { + PRINT_LOG("%%%zu", prev->_insn_id); + PRINT_LOG(" = "); + } + print_ir_insn(prev); + PRINT_LOG("\n"); + } else { + PRINT_LOG(" (No instruction)\n"); + } + PRINT_LOG(" "); + if (!is_void(insn)) { + PRINT_LOG("%%%zu", insn->_insn_id); + PRINT_LOG(" = "); + } + print_ir_insn(insn); + PRINT_LOG(" <--- "); + if (msg) { + PRINT_LOG("%s\n", msg); + } else { + PRINT_LOG("Error\n"); + } + if (next) { + PRINT_LOG(" "); + if (!is_void(next)) { + PRINT_LOG("%%%zu", next->_insn_id); + PRINT_LOG(" = "); + } + print_ir_insn(next); + PRINT_LOG("\n"); + } else { + PRINT_LOG(" (No instruction)\n"); + } +} + +void print_ir_err_init(struct ir_function *fun) +{ + tag_ir(fun); +} + +void print_ir_bb_err(struct ir_basic_block *bb) +{ + PRINT_LOG("BB %zu encountered an error:\n", bb->_id); } diff --git a/IR/ir_insn.c b/IR/ir_insn.c index 1ea880bc..7c318161 100644 --- a/IR/ir_insn.c +++ b/IR/ir_insn.c @@ -1,466 +1,587 @@ -#include "ir_insn.h" -#include -#include "array.h" -#include "bpf_ir.h" -#include "code_gen.h" -#include "dbg.h" -#include "ir_bb.h" -#include "list.h" -#include "ir_helper.h" - -struct ir_insn *create_insn_base(struct ir_basic_block *bb) { - struct ir_insn *new_insn = __malloc(sizeof(struct ir_insn)); - new_insn->parent_bb = bb; - new_insn->users = INIT_ARRAY(struct ir_insn *); - new_insn->value_num = 0; - return new_insn; -} - -struct ir_insn *create_insn_base_cg(struct ir_basic_block *bb) { - struct ir_insn *new_insn = create_insn_base(bb); - init_insn_cg(new_insn); - insn_cg(new_insn)->dst = new_insn; - return new_insn; -} - -void replace_operand(struct ir_insn *insn, struct ir_value v1, struct ir_value v2) { - // Replace v1 with v2 in insn - if (v1.type == IR_VALUE_INSN) { - // Remove user from v1 - val_remove_user(v1, insn); - } - if (v2.type == IR_VALUE_INSN) { - val_add_user(v2, insn); - } -} - -void replace_all_usage(struct ir_insn *insn, struct ir_value rep) { - struct ir_insn **pos; - struct array users = insn->users; - insn->users = INIT_ARRAY(struct ir_insn *); - array_for(pos, users) { - struct ir_insn *user = *pos; - struct array operands = get_operands(user); - struct ir_value **pos2; - array_for(pos2, operands) { - if ((*pos2)->type == IR_VALUE_INSN && (*pos2)->data.insn_d == insn) { - // Match, replace - **pos2 = rep; - val_add_user(rep, user); - } - } - array_free(&operands); - } - array_free(&users); -} - -void replace_all_usage_except(struct ir_insn *insn, struct ir_value rep, struct ir_insn *except) { - struct ir_insn **pos; - struct array users = insn->users; - insn->users = INIT_ARRAY(struct ir_insn *); - array_for(pos, users) { - struct ir_insn *user = *pos; - if (user == except) { - array_push(&insn->users, &user); - continue; - } - struct array operands = get_operands(user); - struct ir_value **pos2; - array_for(pos2, operands) { - if ((*pos2)->type == IR_VALUE_INSN && (*pos2)->data.insn_d == insn) { - // Match, replace - **pos2 = rep; - val_add_user(rep, user); - } - } - array_free(&operands); - } - array_free(&users); -} - -struct array get_operands(struct ir_insn *insn) { - struct array uses = INIT_ARRAY(struct ir_value *); - struct ir_value *pos; - - for (__u8 j = 0; j < insn->value_num; ++j) { - pos = &insn->values[j]; - array_push(&uses, &pos); - } - if (insn->op == IR_INSN_PHI) { - struct phi_value *pv_pos2; - array_for(pv_pos2, insn->phi) { - pos = &pv_pos2->value; - array_push(&uses, &pos); - } - } - return uses; -} - -__u8 is_last_insn(struct ir_insn *insn) { - return insn->parent_bb->ir_insn_head.prev == &insn->list_ptr; -} - -void erase_insn_raw(struct ir_insn *insn) { - list_del(&insn->list_ptr); - __free(insn); -} - -void erase_insn(struct ir_insn *insn) { - // TODO: remove users - struct array operands = get_operands(insn); - struct ir_value **pos2; - array_for(pos2, operands) { - val_remove_user((**pos2), insn); - } - array_free(&operands); - list_del(&insn->list_ptr); - __free(insn); -} - -void insert_at(struct ir_insn *new_insn, struct ir_insn *insn, enum insert_position pos) { - if (pos == INSERT_BACK) { - list_add(&new_insn->list_ptr, &insn->list_ptr); - } else if (pos == INSERT_FRONT) { - list_add_tail(&new_insn->list_ptr, &insn->list_ptr); - } else { - CRITICAL("Insert position not available for insn"); - } -} - -void insert_at_bb(struct ir_insn *new_insn, struct ir_basic_block *bb, enum insert_position pos) { - if (pos == INSERT_BACK) { - list_add_tail(&new_insn->list_ptr, &bb->ir_insn_head); - } else if (pos == INSERT_FRONT) { - list_add(&new_insn->list_ptr, &bb->ir_insn_head); - } else if (pos == INSERT_BACK_BEFORE_JMP) { - // 1. If no JMP instruction, directly insert at the back - // 2. If there is a JMP at the end, insert before it - struct ir_insn *last_insn = get_last_insn(bb); - if (last_insn) { - if (is_jmp(last_insn)) { - // Insert before this insn - list_add_tail(&new_insn->list_ptr, &last_insn->list_ptr); - } else { - // Insert at the back - list_add_tail(&new_insn->list_ptr, &bb->ir_insn_head); - } - } else { - // Empty - list_add_tail(&new_insn->list_ptr, &bb->ir_insn_head); - } - } else if (pos == INSERT_FRONT_AFTER_PHI) { - // Insert after all PHIs - struct ir_insn *insn = NULL; - list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { - if (insn->op != IR_INSN_PHI) { - break; - } - } - if (insn) { - // Insert before insn - list_add_tail(&new_insn->list_ptr, &insn->list_ptr); - } else { - // No insn - list_add(&new_insn->list_ptr, &bb->ir_insn_head); - } - } -} - -struct ir_insn *create_alloc_insn_base(struct ir_basic_block *bb, enum ir_vr_type type) { - struct ir_insn *new_insn = create_insn_base(bb); - new_insn->op = IR_INSN_ALLOC; - new_insn->vr_type = type; - return new_insn; +#include + +// May have exception +struct ir_insn *create_insn_base(struct ir_basic_block *bb) +{ + struct ir_insn *new_insn = malloc_proto(sizeof(struct ir_insn)); + if (!new_insn) { + return NULL; + } + new_insn->parent_bb = bb; + INIT_ARRAY(&new_insn->users, struct ir_insn *); + new_insn->value_num = 0; + return new_insn; +} + +// May have exception +struct ir_insn *create_insn_base_cg(struct ir_basic_block *bb) +{ + struct ir_insn *new_insn = create_insn_base(bb); + if (!new_insn) { + return NULL; + } + + if (bpf_ir_init_insn_cg(new_insn)) { + return NULL; + } + insn_cg(new_insn)->dst = new_insn; + return new_insn; +} + +void replace_operand(struct ir_insn *insn, struct ir_value v1, + struct ir_value v2) +{ + // Replace v1 with v2 in insn + if (v1.type == IR_VALUE_INSN) { + // Remove user from v1 + val_remove_user(v1, insn); + } + if (v2.type == IR_VALUE_INSN) { + val_add_user(v2, insn); + } +} + +void replace_all_usage(struct ir_insn *insn, struct ir_value rep) +{ + struct ir_insn **pos; + struct array users = insn->users; + INIT_ARRAY(&insn->users, struct ir_insn *); + array_for(pos, users) + { + struct ir_insn *user = *pos; + struct array operands = get_operands(user); + struct ir_value **pos2; + array_for(pos2, operands) + { + if ((*pos2)->type == IR_VALUE_INSN && + (*pos2)->data.insn_d == insn) { + // Match, replace + **pos2 = rep; + val_add_user(rep, user); + } + } + bpf_ir_array_free(&operands); + } + bpf_ir_array_free(&users); +} + +void replace_all_usage_except(struct ir_insn *insn, struct ir_value rep, + struct ir_insn *except) +{ + struct ir_insn **pos; + struct array users = insn->users; + INIT_ARRAY(&insn->users, struct ir_insn *); + array_for(pos, users) + { + struct ir_insn *user = *pos; + if (user == except) { + bpf_ir_array_push(&insn->users, &user); + continue; + } + struct array operands = get_operands(user); + struct ir_value **pos2; + array_for(pos2, operands) + { + if ((*pos2)->type == IR_VALUE_INSN && + (*pos2)->data.insn_d == insn) { + // Match, replace + **pos2 = rep; + val_add_user(rep, user); + } + } + bpf_ir_array_free(&operands); + } + bpf_ir_array_free(&users); +} + +struct array get_operands(struct ir_insn *insn) +{ + struct array uses; + INIT_ARRAY(&uses, struct ir_value *); + struct ir_value *pos; + + for (__u8 j = 0; j < insn->value_num; ++j) { + pos = &insn->values[j]; + bpf_ir_array_push(&uses, &pos); + } + if (insn->op == IR_INSN_PHI) { + struct phi_value *pv_pos2; + array_for(pv_pos2, insn->phi) + { + pos = &pv_pos2->value; + bpf_ir_array_push(&uses, &pos); + } + } + if (insn->op == IR_INSN_LOADRAW || insn->op == IR_INSN_STORERAW) { + pos = &insn->addr_val.value; + bpf_ir_array_push(&uses, &pos); + } + return uses; +} + +int is_last_insn(struct ir_insn *insn) +{ + return insn->parent_bb->ir_insn_head.prev == &insn->list_ptr; +} + +void erase_insn_raw(struct ir_insn *insn) +{ + list_del(&insn->list_ptr); + free_proto(insn); +} + +void erase_insn(struct ir_insn *insn) +{ + // TODO: remove users + struct array operands = get_operands(insn); + struct ir_value **pos2; + array_for(pos2, operands) + { + val_remove_user((**pos2), insn); + } + bpf_ir_array_free(&operands); + list_del(&insn->list_ptr); + free_proto(insn); +} + +void insert_at(struct ir_insn *new_insn, struct ir_insn *insn, + enum insert_position pos) +{ + if (pos == INSERT_BACK) { + list_add(&new_insn->list_ptr, &insn->list_ptr); + } else if (pos == INSERT_FRONT) { + list_add_tail(&new_insn->list_ptr, &insn->list_ptr); + } else { + CRITICAL("Insert position not available for insn"); + } +} + +void insert_at_bb(struct ir_insn *new_insn, struct ir_basic_block *bb, + enum insert_position pos) +{ + if (pos == INSERT_BACK) { + list_add_tail(&new_insn->list_ptr, &bb->ir_insn_head); + } else if (pos == INSERT_FRONT) { + list_add(&new_insn->list_ptr, &bb->ir_insn_head); + } else if (pos == INSERT_BACK_BEFORE_JMP) { + // 1. If no JMP instruction, directly insert at the back + // 2. If there is a JMP at the end, insert before it + struct ir_insn *last_insn = bpf_ir_get_last_insn(bb); + if (last_insn) { + if (is_jmp(last_insn)) { + // Insert before this insn + list_add_tail(&new_insn->list_ptr, + &last_insn->list_ptr); + } else { + // Insert at the back + list_add_tail(&new_insn->list_ptr, + &bb->ir_insn_head); + } + } else { + // Empty + list_add_tail(&new_insn->list_ptr, &bb->ir_insn_head); + } + } else if (pos == INSERT_FRONT_AFTER_PHI) { + // Insert after all PHIs + struct ir_insn *insn = NULL; + list_for_each_entry(insn, &bb->ir_insn_head, list_ptr) { + if (insn->op != IR_INSN_PHI) { + break; + } + } + if (insn) { + // Insert before insn + list_add_tail(&new_insn->list_ptr, &insn->list_ptr); + } else { + // No insn + list_add(&new_insn->list_ptr, &bb->ir_insn_head); + } + } +} + +struct ir_insn *create_alloc_insn_base(struct ir_basic_block *bb, + enum ir_vr_type type) +{ + struct ir_insn *new_insn = create_insn_base(bb); + new_insn->op = IR_INSN_ALLOC; + new_insn->vr_type = type; + return new_insn; } struct ir_insn *create_alloc_insn(struct ir_insn *insn, enum ir_vr_type type, - enum insert_position pos) { - struct ir_insn *new_insn = create_alloc_insn_base(insn->parent_bb, type); - insert_at(new_insn, insn, pos); - return new_insn; -} - -struct ir_insn *create_alloc_insn_bb(struct ir_basic_block *bb, enum ir_vr_type type, - enum insert_position pos) { - struct ir_insn *new_insn = create_alloc_insn_base(bb, type); - insert_at_bb(new_insn, bb, pos); - return new_insn; -} - -void val_remove_user(struct ir_value val, struct ir_insn *user) { - if (val.type != IR_VALUE_INSN) { - return; - } - struct array *arr = &val.data.insn_d->users; - for (size_t i = 0; i < arr->num_elem; ++i) { - struct ir_insn *pos = ((struct ir_insn **)(arr->data))[i]; - if (pos == user) { - array_erase(arr, i); - return; - } - } - printf("Warning: User not found in the users\n"); -} - -void val_add_user(struct ir_value val, struct ir_insn *user) { - if (val.type != IR_VALUE_INSN) { - return; - } - array_push_unique(&val.data.insn_d->users, &user); -} - -struct ir_insn *create_store_insn_base(struct ir_basic_block *bb, struct ir_insn *insn, - struct ir_value val) { - struct ir_insn *new_insn = create_insn_base(bb); - new_insn->op = IR_INSN_STORE; - struct ir_value nv = ir_value_insn(insn); - new_insn->values[0] = nv; - new_insn->values[1] = val; - new_insn->value_num = 2; - val_add_user(nv, new_insn); - return new_insn; + enum insert_position pos) +{ + struct ir_insn *new_insn = + create_alloc_insn_base(insn->parent_bb, type); + insert_at(new_insn, insn, pos); + return new_insn; +} + +struct ir_insn *create_alloc_insn_bb(struct ir_basic_block *bb, + enum ir_vr_type type, + enum insert_position pos) +{ + struct ir_insn *new_insn = create_alloc_insn_base(bb, type); + insert_at_bb(new_insn, bb, pos); + return new_insn; +} + +void val_remove_user(struct ir_value val, struct ir_insn *user) +{ + if (val.type != IR_VALUE_INSN) { + return; + } + struct array *arr = &val.data.insn_d->users; + for (size_t i = 0; i < arr->num_elem; ++i) { + struct ir_insn *pos = ((struct ir_insn **)(arr->data))[i]; + if (pos == user) { + bpf_ir_array_erase(arr, i); + return; + } + } + PRINT_LOG("Warning: User not found in the users\n"); +} + +void val_add_user(struct ir_value val, struct ir_insn *user) +{ + if (val.type != IR_VALUE_INSN) { + return; + } + bpf_ir_array_push_unique(&val.data.insn_d->users, &user); +} + +struct ir_insn *create_store_insn_base(struct ir_basic_block *bb, + struct ir_insn *insn, + struct ir_value val) +{ + struct ir_insn *new_insn = create_insn_base(bb); + new_insn->op = IR_INSN_STORE; + struct ir_value nv = bpf_ir_value_insn(insn); + new_insn->values[0] = nv; + new_insn->values[1] = val; + new_insn->value_num = 2; + val_add_user(nv, new_insn); + val_add_user(val, new_insn); + return new_insn; } struct ir_insn *create_store_insn(struct ir_insn *insn, struct ir_insn *st_insn, - struct ir_value val, enum insert_position pos) { - struct ir_insn *new_insn = create_store_insn_base(insn->parent_bb, st_insn, val); - insert_at(new_insn, insn, pos); - return new_insn; -} - -struct ir_insn *create_store_insn_bb(struct ir_basic_block *bb, struct ir_insn *st_insn, - struct ir_value val, enum insert_position pos) { - struct ir_insn *new_insn = create_store_insn_base(bb, st_insn, val); - insert_at_bb(new_insn, bb, pos); - return new_insn; -} - -struct ir_insn *create_load_insn_base(struct ir_basic_block *bb, enum ir_vr_type ty, - struct ir_value val) { - struct ir_insn *new_insn = create_insn_base(bb); - new_insn->op = IR_INSN_LOAD; - new_insn->vr_type = ty; - new_insn->values[0] = val; - val_add_user(val, new_insn); - new_insn->value_num = 1; - return new_insn; -} - -struct ir_insn *create_load_insn(struct ir_insn *insn, enum ir_vr_type ty, struct ir_value val, - enum insert_position pos) { - struct ir_insn *new_insn = create_load_insn_base(insn->parent_bb, ty, val); - insert_at(new_insn, insn, pos); - return new_insn; -} - -struct ir_insn *create_load_insn_bb(struct ir_basic_block *bb, enum ir_vr_type ty, - struct ir_value val, enum insert_position pos) { - struct ir_insn *new_insn = create_load_insn_base(bb, ty, val); - insert_at_bb(new_insn, bb, pos); - return new_insn; -} - -struct ir_insn *create_bin_insn_base(struct ir_basic_block *bb, struct ir_value val1, - struct ir_value val2, enum ir_insn_type ty) { - struct ir_insn *new_insn = create_insn_base(bb); - new_insn->op = ty; - new_insn->values[0] = val1; - new_insn->values[1] = val2; - val_add_user(val1, new_insn); - val_add_user(val2, new_insn); - new_insn->value_num = 2; - return new_insn; -} - -struct ir_insn *create_bin_insn(struct ir_insn *insn, struct ir_value val1, struct ir_value val2, - enum ir_insn_type ty, enum insert_position pos) { - struct ir_insn *new_insn = create_bin_insn_base(insn->parent_bb, val1, val2, ty); - insert_at(new_insn, insn, pos); - return new_insn; -} - -struct ir_insn *create_bin_insn_bb(struct ir_basic_block *bb, struct ir_value val1, - struct ir_value val2, enum ir_insn_type ty, - enum insert_position pos) { - struct ir_insn *new_insn = create_bin_insn_base(bb, val1, val2, ty); - insert_at_bb(new_insn, bb, pos); - return new_insn; -} - -struct ir_insn *prev_insn(struct ir_insn *insn) { - struct list_head *prev = insn->list_ptr.prev; - if (prev == &insn->parent_bb->ir_insn_head) { - return NULL; - } - return list_entry(prev, struct ir_insn, list_ptr); -} - -struct ir_insn *next_insn(struct ir_insn *insn) { - struct list_head *next = insn->list_ptr.next; - if (next == &insn->parent_bb->ir_insn_head) { - return NULL; - } - return list_entry(next, struct ir_insn, list_ptr); -} - -struct ir_insn *create_ja_insn_base(struct ir_basic_block *bb, struct ir_basic_block *to_bb) { - struct ir_insn *new_insn = create_insn_base(bb); - new_insn->op = IR_INSN_JA; - new_insn->bb1 = to_bb; - array_push(&to_bb->users, &new_insn); - return new_insn; -} - -struct ir_insn *create_ja_insn(struct ir_insn *insn, struct ir_basic_block *to_bb, - enum insert_position pos) { - struct ir_insn *new_insn = create_ja_insn_base(insn->parent_bb, to_bb); - insert_at(new_insn, insn, pos); - return new_insn; -} - -struct ir_insn *create_ja_insn_bb(struct ir_basic_block *bb, struct ir_basic_block *to_bb, - enum insert_position pos) { - struct ir_insn *new_insn = create_ja_insn_base(bb, to_bb); - insert_at_bb(new_insn, bb, pos); - return new_insn; -} - -struct ir_insn *create_jbin_insn_base(struct ir_basic_block *bb, struct ir_value val1, - struct ir_value val2, struct ir_basic_block *to_bb1, - struct ir_basic_block *to_bb2, enum ir_insn_type ty) { - struct ir_insn *new_insn = create_insn_base(bb); - new_insn->op = ty; - new_insn->values[0] = val1; - new_insn->values[1] = val2; - new_insn->bb1 = to_bb1; - new_insn->bb2 = to_bb2; - val_add_user(val1, new_insn); - val_add_user(val2, new_insn); - array_push(&to_bb1->users, &new_insn); - array_push(&to_bb2->users, &new_insn); - new_insn->value_num = 2; - return new_insn; -} - -struct ir_insn *create_jbin_insn(struct ir_insn *insn, struct ir_value val1, struct ir_value val2, - struct ir_basic_block *to_bb1, struct ir_basic_block *to_bb2, - enum ir_insn_type ty, enum insert_position pos) { - struct ir_insn *new_insn = - create_jbin_insn_base(insn->parent_bb, val1, val2, to_bb1, to_bb2, ty); - insert_at(new_insn, insn, pos); - return new_insn; -} - -struct ir_insn *create_jbin_insn_bb(struct ir_basic_block *bb, struct ir_value val1, - struct ir_value val2, struct ir_basic_block *to_bb1, - struct ir_basic_block *to_bb2, enum ir_insn_type ty, - enum insert_position pos) { - struct ir_insn *new_insn = create_jbin_insn_base(bb, val1, val2, to_bb1, to_bb2, ty); - insert_at_bb(new_insn, bb, pos); - return new_insn; -} - -struct ir_insn *create_ret_insn_base(struct ir_basic_block *bb, struct ir_value val) { - struct ir_insn *new_insn = create_insn_base(bb); - new_insn->op = IR_INSN_RET; - new_insn->values[0] = val; - new_insn->value_num = 1; - val_add_user(val, new_insn); - return new_insn; + struct ir_value val, enum insert_position pos) +{ + struct ir_insn *new_insn = + create_store_insn_base(insn->parent_bb, st_insn, val); + insert_at(new_insn, insn, pos); + return new_insn; +} + +struct ir_insn *create_store_insn_bb(struct ir_basic_block *bb, + struct ir_insn *st_insn, + struct ir_value val, + enum insert_position pos) +{ + struct ir_insn *new_insn = create_store_insn_base(bb, st_insn, val); + insert_at_bb(new_insn, bb, pos); + return new_insn; +} + +struct ir_insn *create_load_insn_base(struct ir_basic_block *bb, + struct ir_value val) +{ + struct ir_insn *new_insn = create_insn_base(bb); + new_insn->op = IR_INSN_LOAD; + new_insn->values[0] = val; + val_add_user(val, new_insn); + new_insn->value_num = 1; + return new_insn; +} + +struct ir_insn *create_load_insn(struct ir_insn *insn, struct ir_value val, + enum insert_position pos) +{ + struct ir_insn *new_insn = create_load_insn_base(insn->parent_bb, val); + insert_at(new_insn, insn, pos); + return new_insn; +} + +struct ir_insn *create_load_insn_bb(struct ir_basic_block *bb, + struct ir_value val, + enum insert_position pos) +{ + struct ir_insn *new_insn = create_load_insn_base(bb, val); + insert_at_bb(new_insn, bb, pos); + return new_insn; +} + +struct ir_insn *create_bin_insn_base(struct ir_basic_block *bb, + struct ir_value val1, struct ir_value val2, + enum ir_insn_type ty, + enum ir_alu_type aluty) +{ + struct ir_insn *new_insn = create_insn_base(bb); + new_insn->op = ty; + new_insn->values[0] = val1; + new_insn->values[1] = val2; + new_insn->alu = aluty; + val_add_user(val1, new_insn); + val_add_user(val2, new_insn); + new_insn->value_num = 2; + return new_insn; +} + +struct ir_insn *create_bin_insn(struct ir_insn *insn, struct ir_value val1, + struct ir_value val2, enum ir_insn_type ty, + enum ir_alu_type aluty, + enum insert_position pos) +{ + struct ir_insn *new_insn = + create_bin_insn_base(insn->parent_bb, val1, val2, ty, aluty); + insert_at(new_insn, insn, pos); + return new_insn; +} + +struct ir_insn *create_bin_insn_bb(struct ir_basic_block *bb, + struct ir_value val1, struct ir_value val2, + enum ir_insn_type ty, enum ir_alu_type aluty, + enum insert_position pos) +{ + struct ir_insn *new_insn = + create_bin_insn_base(bb, val1, val2, ty, aluty); + insert_at_bb(new_insn, bb, pos); + return new_insn; +} + +struct ir_insn *prev_insn(struct ir_insn *insn) +{ + struct list_head *prev = insn->list_ptr.prev; + if (prev == &insn->parent_bb->ir_insn_head) { + return NULL; + } + return list_entry(prev, struct ir_insn, list_ptr); +} + +struct ir_insn *next_insn(struct ir_insn *insn) +{ + struct list_head *next = insn->list_ptr.next; + if (next == &insn->parent_bb->ir_insn_head) { + return NULL; + } + return list_entry(next, struct ir_insn, list_ptr); +} + +struct ir_insn *create_ja_insn_base(struct ir_basic_block *bb, + struct ir_basic_block *to_bb) +{ + struct ir_insn *new_insn = create_insn_base(bb); + new_insn->op = IR_INSN_JA; + new_insn->bb1 = to_bb; + bpf_ir_array_push(&to_bb->users, &new_insn); + return new_insn; +} + +struct ir_insn *create_ja_insn(struct ir_insn *insn, + struct ir_basic_block *to_bb, + enum insert_position pos) +{ + struct ir_insn *new_insn = create_ja_insn_base(insn->parent_bb, to_bb); + insert_at(new_insn, insn, pos); + return new_insn; +} + +struct ir_insn *create_ja_insn_bb(struct ir_basic_block *bb, + struct ir_basic_block *to_bb, + enum insert_position pos) +{ + struct ir_insn *new_insn = create_ja_insn_base(bb, to_bb); + insert_at_bb(new_insn, bb, pos); + return new_insn; +} + +struct ir_insn * +create_jbin_insn_base(struct ir_basic_block *bb, struct ir_value val1, + struct ir_value val2, struct ir_basic_block *to_bb1, + struct ir_basic_block *to_bb2, enum ir_insn_type ty, + enum ir_alu_type aluty) +{ + struct ir_insn *new_insn = create_insn_base(bb); + new_insn->op = ty; + new_insn->values[0] = val1; + new_insn->values[1] = val2; + new_insn->bb1 = to_bb1; + new_insn->bb2 = to_bb2; + new_insn->alu = aluty; + val_add_user(val1, new_insn); + val_add_user(val2, new_insn); + bpf_ir_array_push(&to_bb1->users, &new_insn); + bpf_ir_array_push(&to_bb2->users, &new_insn); + new_insn->value_num = 2; + return new_insn; +} + +struct ir_insn *create_jbin_insn(struct ir_insn *insn, struct ir_value val1, + struct ir_value val2, + struct ir_basic_block *to_bb1, + struct ir_basic_block *to_bb2, + enum ir_insn_type ty, enum ir_alu_type aluty, + enum insert_position pos) +{ + struct ir_insn *new_insn = create_jbin_insn_base( + insn->parent_bb, val1, val2, to_bb1, to_bb2, ty, aluty); + insert_at(new_insn, insn, pos); + return new_insn; +} + +struct ir_insn * +create_jbin_insn_bb(struct ir_basic_block *bb, struct ir_value val1, + struct ir_value val2, struct ir_basic_block *to_bb1, + struct ir_basic_block *to_bb2, enum ir_insn_type ty, + enum ir_alu_type aluty, enum insert_position pos) +{ + struct ir_insn *new_insn = create_jbin_insn_base(bb, val1, val2, to_bb1, + to_bb2, ty, aluty); + insert_at_bb(new_insn, bb, pos); + return new_insn; +} + +struct ir_insn *create_ret_insn_base(struct ir_basic_block *bb, + struct ir_value val) +{ + struct ir_insn *new_insn = create_insn_base(bb); + new_insn->op = IR_INSN_RET; + new_insn->values[0] = val; + new_insn->value_num = 1; + val_add_user(val, new_insn); + return new_insn; } struct ir_insn *create_ret_insn(struct ir_insn *insn, struct ir_value val, - enum insert_position pos) { - struct ir_insn *new_insn = create_ret_insn_base(insn->parent_bb, val); - insert_at(new_insn, insn, pos); - return new_insn; + enum insert_position pos) +{ + struct ir_insn *new_insn = create_ret_insn_base(insn->parent_bb, val); + insert_at(new_insn, insn, pos); + return new_insn; } -struct ir_insn *create_ret_insn_bb(struct ir_basic_block *bb, struct ir_value val, - enum insert_position pos) { - struct ir_insn *new_insn = create_ret_insn_base(bb, val); - insert_at_bb(new_insn, bb, pos); - return new_insn; +struct ir_insn *create_ret_insn_bb(struct ir_basic_block *bb, + struct ir_value val, + enum insert_position pos) +{ + struct ir_insn *new_insn = create_ret_insn_base(bb, val); + insert_at_bb(new_insn, bb, pos); + return new_insn; } -int is_jmp(struct ir_insn *insn) { - return (insn->op >= IR_INSN_JA && insn->op <= IR_INSN_JNE) || insn->op == IR_INSN_RET; +// Note. This includes ret instruction +int is_jmp(struct ir_insn *insn) +{ + return (insn->op >= IR_INSN_JA && insn->op <= IR_INSN_JNE) || + insn->op == IR_INSN_RET; } -int is_void(struct ir_insn *insn) { - return is_jmp(insn) || insn->op == IR_INSN_STORERAW || insn->op == IR_INSN_STORE; +int is_cond_jmp(struct ir_insn *insn) +{ + return (insn->op >= IR_INSN_JEQ && insn->op < IR_INSN_PHI); } -struct ir_insn *create_assign_insn_base(struct ir_basic_block *bb, struct ir_value val) { - struct ir_insn *new_insn = create_insn_base(bb); - new_insn->op = IR_INSN_ASSIGN; - new_insn->values[0] = val; - new_insn->value_num = 1; - val_add_user(val, new_insn); - return new_insn; +int is_alu(struct ir_insn *insn) +{ + return insn->op >= IR_INSN_ADD && insn->op < IR_INSN_CALL; } -struct ir_insn *create_assign_insn(struct ir_insn *insn, struct ir_value val, - enum insert_position pos) { - struct ir_insn *new_insn = create_assign_insn_base(insn->parent_bb, val); - insert_at(new_insn, insn, pos); - return new_insn; +int is_void(struct ir_insn *insn) +{ + return is_jmp(insn) || insn->op == IR_INSN_STORERAW || + insn->op == IR_INSN_STORE; } -struct ir_insn *create_assign_insn_bb(struct ir_basic_block *bb, struct ir_value val, - enum insert_position pos) { - struct ir_insn *new_insn = create_assign_insn_base(bb, val); - insert_at_bb(new_insn, bb, pos); - return new_insn; +struct ir_insn *create_assign_insn_base(struct ir_basic_block *bb, + struct ir_value val) +{ + struct ir_insn *new_insn = create_insn_base(bb); + new_insn->op = IR_INSN_ASSIGN; + new_insn->values[0] = val; + new_insn->value_num = 1; + val_add_user(val, new_insn); + return new_insn; } -struct ir_insn *create_assign_insn_base_cg(struct ir_basic_block *bb, struct ir_value val) { - struct ir_insn *new_insn = create_insn_base_cg(bb); - new_insn->op = IR_INSN_ASSIGN; - new_insn->values[0] = val; - new_insn->value_num = 1; - val_add_user(val, new_insn); - return new_insn; +struct ir_insn *create_assign_insn(struct ir_insn *insn, struct ir_value val, + enum insert_position pos) +{ + struct ir_insn *new_insn = + create_assign_insn_base(insn->parent_bb, val); + insert_at(new_insn, insn, pos); + return new_insn; +} + +struct ir_insn *create_assign_insn_bb(struct ir_basic_block *bb, + struct ir_value val, + enum insert_position pos) +{ + struct ir_insn *new_insn = create_assign_insn_base(bb, val); + insert_at_bb(new_insn, bb, pos); + return new_insn; +} + +struct ir_insn *create_assign_insn_base_cg(struct ir_basic_block *bb, + struct ir_value val) +{ + struct ir_insn *new_insn = create_insn_base_cg(bb); + new_insn->op = IR_INSN_ASSIGN; + new_insn->values[0] = val; + new_insn->value_num = 1; + new_insn->vr_type = IR_VR_TYPE_64; + val_add_user(val, new_insn); + return new_insn; } struct ir_insn *create_assign_insn_cg(struct ir_insn *insn, struct ir_value val, - enum insert_position pos) { - struct ir_insn *new_insn = create_assign_insn_base_cg(insn->parent_bb, val); - insert_at(new_insn, insn, pos); - return new_insn; -} - -struct ir_insn *create_assign_insn_bb_cg(struct ir_basic_block *bb, struct ir_value val, - enum insert_position pos) { - struct ir_insn *new_insn = create_assign_insn_base_cg(bb, val); - insert_at_bb(new_insn, bb, pos); - return new_insn; -} - -struct ir_insn *create_phi_insn_base(struct ir_basic_block *bb) { - struct ir_insn *new_insn = create_insn_base(bb); - new_insn->op = IR_INSN_PHI; - new_insn->phi = INIT_ARRAY(struct phi_value); - return new_insn; -} - -struct ir_insn *create_phi_insn(struct ir_insn *insn, enum insert_position pos) { - struct ir_insn *new_insn = create_phi_insn_base(insn->parent_bb); - insert_at(new_insn, insn, pos); - return new_insn; -} - -struct ir_insn *create_phi_insn_bb(struct ir_basic_block *bb, enum insert_position pos) { - struct ir_insn *new_insn = create_phi_insn_base(bb); - insert_at_bb(new_insn, bb, pos); - return new_insn; -} - -void phi_add_operand(struct ir_insn *insn, struct ir_basic_block *bb, struct ir_value val) { - // Make sure that bb is a pred of insn parent BB - struct phi_value pv; - pv.value = val; - pv.bb = bb; - array_push(&insn->phi, &pv); - val_add_user(val, insn); + enum insert_position pos) +{ + struct ir_insn *new_insn = + create_assign_insn_base_cg(insn->parent_bb, val); + insert_at(new_insn, insn, pos); + return new_insn; +} + +struct ir_insn *create_assign_insn_bb_cg(struct ir_basic_block *bb, + struct ir_value val, + enum insert_position pos) +{ + struct ir_insn *new_insn = create_assign_insn_base_cg(bb, val); + insert_at_bb(new_insn, bb, pos); + return new_insn; +} + +struct ir_insn *create_phi_insn_base(struct ir_basic_block *bb) +{ + struct ir_insn *new_insn = create_insn_base(bb); + new_insn->op = IR_INSN_PHI; + INIT_ARRAY(&new_insn->phi, struct phi_value); + return new_insn; +} + +struct ir_insn *create_phi_insn(struct ir_insn *insn, enum insert_position pos) +{ + struct ir_insn *new_insn = create_phi_insn_base(insn->parent_bb); + insert_at(new_insn, insn, pos); + return new_insn; +} + +struct ir_insn *create_phi_insn_bb(struct ir_basic_block *bb, + enum insert_position pos) +{ + struct ir_insn *new_insn = create_phi_insn_base(bb); + insert_at_bb(new_insn, bb, pos); + return new_insn; +} + +void phi_add_operand(struct ir_insn *insn, struct ir_basic_block *bb, + struct ir_value val) +{ + // Make sure that bb is a pred of insn parent BB + struct phi_value pv; + pv.value = val; + pv.bb = bb; + bpf_ir_array_push(&insn->phi, &pv); + val_add_user(val, insn); } diff --git a/IR/ir_value.c b/IR/ir_value.c new file mode 100644 index 00000000..6fb46092 --- /dev/null +++ b/IR/ir_value.c @@ -0,0 +1,28 @@ +#include + +__u8 bpf_ir_value_equal(struct ir_value a, struct ir_value b) +{ + if (a.type != b.type) { + return 0; + } + if (a.type == IR_VALUE_CONSTANT) { + return a.data.constant_d == b.data.constant_d; + } + if (a.type == IR_VALUE_INSN) { + return a.data.insn_d == b.data.insn_d; + } + if (a.type == IR_VALUE_STACK_PTR) { + return 1; + } + CRITICAL("Error"); +} + +struct ir_value bpf_ir_value_insn(struct ir_insn *insn) +{ + return (struct ir_value){ .type = IR_VALUE_INSN, .data.insn_d = insn }; +} + +struct ir_value bpf_ir_value_stack_ptr(void) +{ + return (struct ir_value){ .type = IR_VALUE_STACK_PTR }; +} diff --git a/IR/lii.c b/IR/lii.c new file mode 100644 index 00000000..cac9451a --- /dev/null +++ b/IR/lii.c @@ -0,0 +1,13 @@ +#include + +// Kernel-side Low Interface Implementation + +void *malloc_proto(size_t size) +{ + return kvzalloc(size, GFP_KERNEL); +} + +void free_proto(void *ptr) +{ + kvfree(ptr); +} diff --git a/IR/passes/add_constraint_pass.c b/IR/passes/add_constraint_pass.c index 646fe258..8475bfab 100644 --- a/IR/passes/add_constraint_pass.c +++ b/IR/passes/add_constraint_pass.c @@ -1,41 +1,44 @@ -#include "array.h" -#include "bpf_ir.h" -#include "constraint.h" -#include "dbg.h" -#include "ir_bb.h" -#include "ir_insn.h" +#include // Initialize some testing constraints -void init_test_constraints(struct ir_function *fun) { - fun->value_constraints = INIT_ARRAY(struct ir_constraint); +void init_test_constraints(struct ir_function *fun) +{ + INIT_ARRAY(&fun->value_constraints, struct ir_constraint); } -void add_constraint(struct ir_function *fun) { - init_test_constraints(fun); // For testing purpose +void add_constraint(struct ir_function *fun) +{ + init_test_constraints(fun); // For testing purpose - struct ir_basic_block *err_bb = create_bb(fun); - struct ir_value val; - val.type = IR_VALUE_CONSTANT; - val.data.constant_d.type = IR_CONSTANT_U64; - val.data.constant_d.data.u64_d = 1; - create_ret_insn_bb(err_bb, val, INSERT_BACK); + struct ir_basic_block *err_bb = bpf_ir_create_bb(fun); + struct ir_value val; + val.type = IR_VALUE_CONSTANT; + val.data.constant_d = 1; + create_ret_insn_bb(err_bb, val, INSERT_BACK); - struct ir_constraint *pos; - array_for(pos, fun->value_constraints) { - struct ir_constraint c = *pos; - if (c.type == CONSTRAINT_TYPE_VALUE_EQUAL) { - struct ir_basic_block *newbb = split_bb(fun, c.pos); - create_jbin_insn(c.pos, c.val, c.cval, newbb, err_bb, IR_INSN_JNE, INSERT_FRONT); - connect_bb(c.pos->parent_bb, err_bb); - } else if (c.type == CONSTRAINT_TYPE_VALUE_RANGE) { - struct ir_basic_block *newbb = split_bb(fun, c.pos); - create_jbin_insn(c.pos, c.val, c.start, newbb, err_bb, IR_INSN_JLT, INSERT_FRONT); - connect_bb(c.pos->parent_bb, err_bb); - struct ir_basic_block *newbb2 = split_bb(fun, c.pos); - create_jbin_insn(c.pos, c.val, c.end, newbb2, err_bb, IR_INSN_JGE, INSERT_FRONT); - connect_bb(c.pos->parent_bb, err_bb); - } else { - CRITICAL("Error"); - } - } + struct ir_constraint *pos; + array_for(pos, fun->value_constraints) + { + struct ir_constraint c = *pos; + if (c.type == CONSTRAINT_TYPE_VALUE_EQUAL) { + struct ir_basic_block *newbb = + bpf_ir_split_bb(fun, c.pos); + create_jbin_insn(c.pos, c.val, c.cval, newbb, err_bb, + IR_INSN_JNE, IR_ALU_64, INSERT_FRONT); + bpf_ir_connect_bb(c.pos->parent_bb, err_bb); + } else if (c.type == CONSTRAINT_TYPE_VALUE_RANGE) { + struct ir_basic_block *newbb = + bpf_ir_split_bb(fun, c.pos); + create_jbin_insn(c.pos, c.val, c.start, newbb, err_bb, + IR_INSN_JLT, IR_ALU_64, INSERT_FRONT); + bpf_ir_connect_bb(c.pos->parent_bb, err_bb); + struct ir_basic_block *newbb2 = + bpf_ir_split_bb(fun, c.pos); + create_jbin_insn(c.pos, c.val, c.end, newbb2, err_bb, + IR_INSN_JGE, IR_ALU_64, INSERT_FRONT); + bpf_ir_connect_bb(c.pos->parent_bb, err_bb); + } else { + CRITICAL("Error"); + } + } } diff --git a/IR/passes/add_counter_pass.c b/IR/passes/add_counter_pass.c index 2a0453f9..cd7980d9 100644 --- a/IR/passes/add_counter_pass.c +++ b/IR/passes/add_counter_pass.c @@ -1,47 +1,51 @@ -#include "bpf_ir.h" -#include "ir_bb.h" -#include "ir_insn.h" +#include -void add_counter(struct ir_function *fun) { - struct ir_basic_block *entry = fun->entry; - struct ir_insn *alloc_insn = create_alloc_insn_bb(entry, IR_VR_TYPE_64, INSERT_FRONT); - struct ir_value val; - val.type = IR_VALUE_CONSTANT; - val.data.constant_d.type = IR_CONSTANT_U64; - val.data.constant_d.data.u64_d = 0; - create_store_insn(alloc_insn, alloc_insn, val, INSERT_BACK); - struct ir_basic_block **pos; +void add_counter(struct ir_function *fun) +{ + struct ir_basic_block *entry = fun->entry; + struct ir_insn *alloc_insn = + create_alloc_insn_bb(entry, IR_VR_TYPE_64, INSERT_FRONT); + struct ir_value val; + val.type = IR_VALUE_CONSTANT; + val.data.constant_d = 0; + create_store_insn(alloc_insn, alloc_insn, val, INSERT_BACK); + struct ir_basic_block **pos; - struct ir_basic_block *err_bb = create_bb(fun); - val.data.constant_d.data.u64_d = 1; - create_ret_insn_bb(err_bb, val, INSERT_BACK); + struct ir_basic_block *err_bb = bpf_ir_create_bb(fun); + val.data.constant_d = 1; + create_ret_insn_bb(err_bb, val, INSERT_BACK); - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - size_t len = bb_len(bb); - struct ir_insn *last = get_last_insn(bb); - if (!last) { - // No insn in the bb - continue; - } - val.type = IR_VALUE_INSN; - val.data.insn_d = alloc_insn; - struct ir_insn *load_insn = create_load_insn(last, IR_VR_TYPE_64, val, INSERT_FRONT); - struct ir_value val1; - val1.type = IR_VALUE_CONSTANT; - val1.data.constant_d.type = IR_CONSTANT_U64; - val1.data.constant_d.data.u64_d = len; - struct ir_value val2; - val2.type = IR_VALUE_INSN; - val2.data.insn_d = load_insn; - struct ir_insn *added = create_bin_insn(load_insn, val1, val2, IR_INSN_ADD, INSERT_BACK); - val.data.insn_d = added; - struct ir_insn *store_back = create_store_insn(added, alloc_insn, val, INSERT_BACK); - struct ir_basic_block *new_bb = split_bb(fun, store_back); - val2.data.insn_d = added; - val1.data.constant_d.data.u64_d = 0x10000; - create_jbin_insn(store_back, val1, val2, new_bb, err_bb, IR_INSN_JLT, INSERT_BACK); - // Manually connect BBs - connect_bb(bb, err_bb); - } + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + size_t len = bpf_ir_bb_len(bb); + struct ir_insn *last = bpf_ir_get_last_insn(bb); + if (!last) { + // No insn in the bb + continue; + } + struct ir_insn *load_insn = create_load_insn( + last, bpf_ir_value_insn(alloc_insn), INSERT_FRONT); + struct ir_value val1; + val1.type = IR_VALUE_CONSTANT; + val1.data.constant_d = len; + struct ir_value val2; + val2.type = IR_VALUE_INSN; + val2.data.insn_d = load_insn; + struct ir_insn *added = create_bin_insn(load_insn, val1, val2, + IR_INSN_ADD, IR_ALU_64, + INSERT_BACK); + val.data.insn_d = added; + val.type = IR_VALUE_INSN; + struct ir_insn *store_back = + create_store_insn(added, alloc_insn, val, INSERT_BACK); + struct ir_basic_block *new_bb = + bpf_ir_split_bb(fun, store_back); + val2.data.insn_d = added; + val1.data.constant_d = 0x10000; + create_jbin_insn(store_back, val1, val2, new_bb, err_bb, + IR_INSN_JLT, IR_ALU_64, INSERT_BACK); + // Manually connect BBs + bpf_ir_connect_bb(bb, err_bb); + } } diff --git a/IR/passes/cut_bb_pass.c b/IR/passes/cut_bb_pass.c index de91eb67..a01cd152 100644 --- a/IR/passes/cut_bb_pass.c +++ b/IR/passes/cut_bb_pass.c @@ -1,51 +1,56 @@ -#include "array.h" -#include "bpf_ir.h" -#include "dbg.h" -#include "passes.h" -#include "list.h" -void cut_bb(struct ir_function *fun) { - struct ir_basic_block **pos; - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - if (list_empty(&bb->ir_insn_head)) { - // Empty BB, try removing! - if (bb->succs.num_elem == 0) { - CRITICAL("Empty BB with no successors"); - } - if (bb->succs.num_elem > 1) { - CRITICAL("Empty BB with > 1 successors"); - } - struct ir_basic_block **pos2; - struct ir_basic_block *next = ((struct ir_basic_block **)(bb->succs.data))[0]; - array_for(pos2, bb->preds) { - struct ir_basic_block *pred = *pos2; - struct ir_basic_block **pos3; - array_for(pos3, pred->succs) { - struct ir_basic_block *succ = *pos3; - if (succ == bb) { - *pos3 = next; - } - } - } - struct ir_insn **pos4; - array_for(pos4, bb->users) { - struct ir_insn *user = *pos4; - if (user->bb1 == bb) { - user->bb1 = next; - } - if (user->bb2 == bb) { - user->bb2 = next; - } - if (user->op == IR_INSN_PHI) { - struct phi_value *pos5; - array_for(pos5, user->phi) { - if (pos5->bb == bb) { - pos5->bb = next; - } - } - } - } - } - } +#include + +// Warning: Not usable now +void cut_bb(struct ir_function *fun) +{ + struct ir_basic_block **pos; + array_for(pos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *pos; + if (list_empty(&bb->ir_insn_head)) { + // Empty BB, try removing! + if (bb->succs.num_elem == 0) { + CRITICAL("Empty BB with no successors"); + } + if (bb->succs.num_elem > 1) { + CRITICAL("Empty BB with > 1 successors"); + } + struct ir_basic_block **pos2; + struct ir_basic_block *next = + ((struct ir_basic_block **)(bb->succs.data))[0]; + array_for(pos2, bb->preds) + { + struct ir_basic_block *pred = *pos2; + struct ir_basic_block **pos3; + array_for(pos3, pred->succs) + { + struct ir_basic_block *succ = *pos3; + if (succ == bb) { + *pos3 = next; + } + } + } + struct ir_insn **pos4; + array_for(pos4, bb->users) + { + struct ir_insn *user = *pos4; + if (user->bb1 == bb) { + user->bb1 = next; + } + if (user->bb2 == bb) { + user->bb2 = next; + } + if (user->op == IR_INSN_PHI) { + struct phi_value *pos5; + array_for(pos5, user->phi) + { + if (pos5->bb == bb) { + pos5->bb = next; + } + } + } + } + } + } } diff --git a/IR/passes/end_bb.c b/IR/passes/end_bb.c deleted file mode 100644 index ae737daa..00000000 --- a/IR/passes/end_bb.c +++ /dev/null @@ -1,12 +0,0 @@ -#include "array.h" -#include "ir_fun.h" - -void gen_end_bbs(struct ir_function *fun) { - struct ir_basic_block **pos; - array_for(pos, fun->reachable_bbs) { - struct ir_basic_block *bb = *pos; - if (bb->succs.num_elem == 0) { - array_push(&fun->end_bbs, &bb); - } - } -} diff --git a/IR/passes/phi_pass.c b/IR/passes/phi_pass.c index 70ccf2f2..4db2799c 100644 --- a/IR/passes/phi_pass.c +++ b/IR/passes/phi_pass.c @@ -1,49 +1,49 @@ -#include -#include "array.h" -#include "bpf_ir.h" -#include "dbg.h" -#include "ir_insn.h" -#include "list.h" -#include "passes.h" +#include -void try_remove_trivial_phi(struct ir_insn *phi) { - if (phi->op != IR_INSN_PHI) { - return; - } - // print_raw_ir_insn(phi); - struct ir_value same; - __u8 same_has_value = 0; - struct phi_value *pv_pos; - array_for(pv_pos, phi->phi) { - struct phi_value pv = *pv_pos; - if (pv.value.type == IR_VALUE_INSN && ((same_has_value && same.type == IR_VALUE_INSN && - pv.value.data.insn_d == same.data.insn_d) || - pv.value.data.insn_d == phi)) { - continue; - } - if (same_has_value) { - return; - } - same = pv.value; - same_has_value = 1; - } - // printf("Phi to remove: "); - // print_raw_ir_insn(phi); - if (!same_has_value) { - same.type = IR_VALUE_UNDEF; - } - replace_all_usage_except(phi, same, phi); +void try_remove_trivial_phi(struct ir_insn *phi) +{ + if (phi->op != IR_INSN_PHI) { + return; + } + // print_raw_ir_insn(phi); + struct ir_value same; + __u8 same_has_value = 0; + struct phi_value *pv_pos; + array_for(pv_pos, phi->phi) + { + struct phi_value pv = *pv_pos; + if (pv.value.type == IR_VALUE_INSN && + ((same_has_value && same.type == IR_VALUE_INSN && + pv.value.data.insn_d == same.data.insn_d) || + pv.value.data.insn_d == phi)) { + continue; + } + if (same_has_value) { + return; + } + same = pv.value; + same_has_value = 1; + } + // PRINT_LOG("Phi to remove: "); + // print_raw_ir_insn(phi); + if (!same_has_value) { + same.type = IR_VALUE_UNDEF; + } + replace_all_usage_except(phi, same, phi); - erase_insn(phi); + erase_insn(phi); } -void remove_trivial_phi(struct ir_function *fun) { - printf("PHI removal\n"); - for (size_t i = 0; i < fun->reachable_bbs.num_elem; ++i) { - struct ir_basic_block *bb = ((struct ir_basic_block **)(fun->reachable_bbs.data))[i]; - struct ir_insn *pos, *n; - list_for_each_entry_safe(pos, n, &bb->ir_insn_head, list_ptr) { - try_remove_trivial_phi(pos); - } - } +void remove_trivial_phi(struct ir_function *fun) +{ + struct ir_basic_block **bpos; + array_for(bpos, fun->reachable_bbs) + { + struct ir_basic_block *bb = *bpos; + struct ir_insn *pos, *tmp; + list_for_each_entry_safe(pos, tmp, &bb->ir_insn_head, + list_ptr) { + try_remove_trivial_phi(pos); + } + } } diff --git a/IR/passes/reachable_bb.c b/IR/passes/reachable_bb.c deleted file mode 100644 index 65fa20e5..00000000 --- a/IR/passes/reachable_bb.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "array.h" -#include "dbg.h" -#include "passes.h" - -void add_reach(struct ir_function *fun, struct ir_basic_block *bb) { - if (bb->_visited) { - return; - } - bb->_visited = 1; - array_push(&fun->reachable_bbs, &bb); - - struct ir_basic_block **succ; - __u8 i = 0; - array_for(succ, bb->succs) { - if (i == 0) { - i = 1; - // Check if visited - if ((*succ)->_visited) { - CRITICAL("Loop BB detected"); - } - } - add_reach(fun, *succ); - } -} - -void gen_reachable_bbs(struct ir_function *fun) { - array_free(&fun->reachable_bbs); - fun->reachable_bbs = INIT_ARRAY(struct ir_basic_block *); - add_reach(fun, fun->entry); -} diff --git a/IR/probe.c b/IR/probe.c index b523b900..d3528e48 100644 --- a/IR/probe.c +++ b/IR/probe.c @@ -1,15 +1,18 @@ #include #include -#include "read.h" +#include -void run(struct bpf_insn *insns, size_t len) { - printf("CLASS\tSIZE\tMODE\tOP\tSRC\tIMM\n"); - for (size_t i = 0; i < len; ++i) { - __u8 code = insns[i].code; - // if (code == 0) { - // continue; - // } - printf("%02x\t%02x\t%02x\t%02x\t%02x\t%d\n", BPF_CLASS(code), BPF_SIZE(code), - BPF_MODE(code), BPF_OP(code), BPF_SRC(code), insns[i].imm); - } +int bpf_ir_run(struct bpf_insn *insns, size_t len) +{ + printf("CLASS\tSIZE\tMODE\tOP\tSRC\tIMM\n"); + for (size_t i = 0; i < len; ++i) { + __u8 code = insns[i].code; + // if (code == 0) { + // continue; + // } + printf("%02x\t%02x\t%02x\t%02x\t%02x\t%d\n", BPF_CLASS(code), + BPF_SIZE(code), BPF_MODE(code), BPF_OP(code), + BPF_SRC(code), insns[i].imm); + } + return 0; } diff --git a/IR/read.c b/IR/read.c index 0520120e..3e3071cd 100644 --- a/IR/read.c +++ b/IR/read.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -5,64 +6,85 @@ #include #include #include -#include "read.h" +#include -void print_item(FILE *fd, Elf64_Ehdr eh, Elf64_Shdr sh_table[]) { - int i = 0; - char *sh_str = NULL; - char *buff = NULL; +void *malloc_proto(size_t size) +{ + void *data = malloc(size); + if (data) { + memset(data, 0, size); + } + return data; +} + +void free_proto(void *ptr) +{ + free(ptr); +} + +void print_item(FILE *fd, Elf64_Ehdr eh, Elf64_Shdr sh_table[]) +{ + int i = 0; + char *sh_str = NULL; + char *buff = NULL; - buff = malloc(sh_table[eh.e_shstrndx].sh_size); + buff = malloc(sh_table[eh.e_shstrndx].sh_size); - if (buff != NULL) { - fseek(fd, sh_table[eh.e_shstrndx].sh_offset, SEEK_SET); - fread(buff, 1, sh_table[eh.e_shstrndx].sh_size, fd); - } - sh_str = buff; + if (buff != NULL) { + fseek(fd, sh_table[eh.e_shstrndx].sh_offset, SEEK_SET); + fread(buff, 1, sh_table[eh.e_shstrndx].sh_size, fd); + } + sh_str = buff; - for (i = 0; i < eh.e_shnum; i++) { - if (!strcmp("xdp", (sh_str + sh_table[i].sh_name))) { - printf("Found section\t\".text\"\n"); - printf("at offset\t0x%08x\n", (unsigned int)sh_table[i].sh_offset); - printf("of size\t\t0x%08x\n", (unsigned int)sh_table[i].sh_size); - break; - } - } + for (i = 0; i < eh.e_shnum; i++) { + if (!strcmp("xdp", (sh_str + sh_table[i].sh_name))) { + printf("Found section\t\".text\"\n"); + printf("at offset\t0x%08x\n", + (unsigned int)sh_table[i].sh_offset); + printf("of size\t\t0x%08x\n", + (unsigned int)sh_table[i].sh_size); + break; + } + } - if (i < eh.e_shnum) { - uint64_t size = sh_table[i].sh_size; - uint32_t insn_cnt = size / sizeof(struct bpf_insn); - char *mydata = malloc(size); - fseek(fd, sh_table[i].sh_offset, SEEK_SET); - fread(mydata, 1, size, fd); - struct bpf_insn *prog = (struct bpf_insn *)mydata; - run(prog, insn_cnt); - free(mydata); - } - free(buff); + if (i < eh.e_shnum) { + uint64_t size = sh_table[i].sh_size; + uint32_t insn_cnt = size / sizeof(struct bpf_insn); + char *mydata = malloc(size); + fseek(fd, sh_table[i].sh_offset, SEEK_SET); + fread(mydata, 1, size, fd); + struct bpf_insn *prog = (struct bpf_insn *)mydata; + bpf_ir_run(prog, insn_cnt); + free(mydata); + } + free(buff); } -int main(int argc, char **argv) { - if (argc <= 1) { - return -1; - } - FILE *fp = NULL; // Pointer used to access current file - char *program_name = NULL; - Elf64_Shdr *sh_table = NULL; // Elf symbol table - Elf64_Ehdr elf_header; // Elf header +int main(int argc, char **argv) +{ + if (argc <= 1) { + return -1; + } + FILE *fp = NULL; // Pointer used to access current file + char *program_name = NULL; + Elf64_Shdr *sh_table = NULL; // Elf symbol table + Elf64_Ehdr elf_header; // Elf header - program_name = argv[1]; - fp = fopen(program_name, "r"); + program_name = argv[1]; + fp = fopen(program_name, "r"); + if (!fp) { + return -1; + } - fseek(fp, 0, SEEK_SET); - fread(&elf_header, 1, sizeof(Elf64_Ehdr), fp); - sh_table = malloc(elf_header.e_shentsize * elf_header.e_shnum); + fseek(fp, 0, SEEK_SET); + fread(&elf_header, 1, sizeof(Elf64_Ehdr), fp); + sh_table = malloc(elf_header.e_shentsize * elf_header.e_shnum); - fseek(fp, elf_header.e_shoff, SEEK_SET); - fread(sh_table, 1, elf_header.e_shentsize * elf_header.e_shnum, fp); + fseek(fp, elf_header.e_shoff, SEEK_SET); + fread(sh_table, 1, elf_header.e_shentsize * elf_header.e_shnum, fp); - print_item(fp, elf_header, sh_table); - free(sh_table); - fclose(fp); - return 0; + print_item(fp, elf_header, sh_table); + free(sh_table); + fclose(fp); + return 0; } diff --git a/IR/format.sh b/IR/scripts/format.sh similarity index 100% rename from IR/format.sh rename to IR/scripts/format.sh diff --git a/IR/scripts/gen_kernel.sh b/IR/scripts/gen_kernel.sh new file mode 100755 index 00000000..e46e6c17 --- /dev/null +++ b/IR/scripts/gen_kernel.sh @@ -0,0 +1,47 @@ +#!/bin/bash + +# Generating kernel source files + +KERNEL_PATH=/home/linsy/Projects/ebpf/eBPF-kernel + +rm -rf build/kernel + +mkdir -p build/kernel + +files=$(find . -iname '*.c' -not -path "./build/*" -not -path "./tests/*") + +for file in $files; do + cp $file build/kernel +done + + +cd build/kernel + +rm read.c probe.c + +cfiles=$(ls *.c) + +filelist="" + +for file in $cfiles; do + filelist="${filelist} ${file::-1}o" +done + +echo $filelist + +makefile_content=""" +obj-y :=$filelist +""" + +echo $makefile_content > Makefile +# Remove redundant files + +rm -rf ${KERNEL_PATH}/kernel/bpf/ir/ +mkdir ${KERNEL_PATH}/kernel/bpf/ir/ +cp * ${KERNEL_PATH}/kernel/bpf/ir/ + +cd ../../ + +rm ${KERNEL_PATH}/include/linux/bpf_ir.h + +cp include/linux/bpf_ir.h ${KERNEL_PATH}/include/linux/ diff --git a/IR/tests/alu64.c b/IR/tests/alu64.c index 04f77f2c..8cd7da94 100644 --- a/IR/tests/alu64.c +++ b/IR/tests/alu64.c @@ -2,18 +2,19 @@ #include SEC("xdp") -int prog(void *ctx) { - char s2[] = "1"; - bpf_trace_printk(s2, sizeof(s2)); - char s1[] = "hello world\n"; - // bpf_trace_printk(s1, sizeof(s1)); - __u64 i1 = 0x3456789abcdef0; - __u64 i2 = 0x76543210fedcba; - __u64 ans = i1 + i2; - // char s[10] = {}; +int prog(void *ctx) +{ + char s2[] = "1"; + bpf_trace_printk(s2, sizeof(s2)); + char s1[] = "hello world\n"; + // bpf_trace_printk(s1, sizeof(s1)); + __u64 i1 = 0x3456789abcdef0; + __u64 i2 = 0x76543210fedcba; + __u64 ans = i1 + i2; + // char s[10] = {}; - // int i = 2; - return 0; + // int i = 2; + return 0; } char _license[] SEC("license") = "GPL"; diff --git a/IR/tests/b2b.c b/IR/tests/b2b.c index 3772ba87..3a289aff 100644 --- a/IR/tests/b2b.c +++ b/IR/tests/b2b.c @@ -1,22 +1,25 @@ #include #include -void __noinline call1() { - bpf_trace_printk("hello world", 2); +void __noinline call1() +{ + bpf_trace_printk("hello world", 2); } -void __noinline call2() { - bpf_trace_printk("world", 6); +void __noinline call2() +{ + bpf_trace_printk("world", 6); } SEC("xdp") -int prog(void *ctx) { - // char s1[] = "hello world my friend"; - char s1[] = "1"; - bpf_trace_printk(s1, sizeof(s1)); - call1(); - call2(); - return 0; +int prog(void *ctx) +{ + // char s1[] = "hello world my friend"; + char s1[] = "1"; + bpf_trace_printk(s1, sizeof(s1)); + call1(); + call2(); + return 0; } char _license[] SEC("license") = "GPL"; diff --git a/IR/tests/loop1.c b/IR/tests/loop1.c index a2cbe6e6..6ad6e093 100644 --- a/IR/tests/loop1.c +++ b/IR/tests/loop1.c @@ -6,14 +6,15 @@ */ SEC("xdp") -int prog(void *ctx) { - __u64 t = bpf_ktime_get_ns(); - bpf_trace_printk(ctx, t); - for (__u64 i = 0; i < t; ++i) { - bpf_trace_printk("s", i); - bpf_trace_printk(ctx, 2); - } - return 0; +int prog(void *ctx) +{ + __u64 t = bpf_ktime_get_ns(); + bpf_trace_printk(ctx, t); + for (__u64 i = 0; i < t; ++i) { + bpf_trace_printk("s", i); + bpf_trace_printk(ctx, 2); + } + return 0; } char _license[] SEC("license") = "GPL"; diff --git a/IR/tests/mem1.c b/IR/tests/mem1.c index a4daefcd..ee70f76a 100644 --- a/IR/tests/mem1.c +++ b/IR/tests/mem1.c @@ -8,17 +8,18 @@ */ SEC("xdp") -int prog(void *ctx) { - int id = bpf_ktime_get_ns() % 20; - int arr[10] = {}; - for (int i = 0; i < 10; ++i) { - arr[i] = i; - } - if (id > 10 || id < 0) { - return 0; - } - bpf_trace_printk("%d", 1, arr[id]); - return 0; +int prog(void *ctx) +{ + int id = bpf_ktime_get_ns() % 20; + int arr[10] = {}; + for (int i = 0; i < 10; ++i) { + arr[i] = i; + } + if (id > 10 || id < 0) { + return 0; + } + bpf_trace_printk("%d", 1, arr[id]); + return 0; } char _license[] SEC("license") = "GPL"; diff --git a/IR/tests/mem2.c b/IR/tests/mem2.c index d26b8337..0f42ed5f 100644 --- a/IR/tests/mem2.c +++ b/IR/tests/mem2.c @@ -1,12 +1,13 @@ #include #include -int __noinline call(__u64 i) { - if (i > 100) { - return -1; - } - bpf_trace_printk("i: %d\n", 4, i); - return 0; +int __noinline call(__u64 i) +{ + if (i > 100) { + return -1; + } + bpf_trace_printk("i: %d\n", 4, i); + return 0; } /** @@ -16,27 +17,28 @@ int __noinline call(__u64 i) { */ SEC("xdp") -int prog(struct xdp_md *ctx) { - int id = bpf_ktime_get_ns() % 20; // We cannot use 10 here - __u64 arr[10] = {}; - for (__u32 i = 0; i < 10; ++i) { - arr[i] = i; - } - call(arr[9]); // Pass - // call(arr[10]); // Not Pass - // call(arr[11]); // Not Pass - if (id > 9 || id < 0) { // Work - goto end; - } - __u64 res = arr[id]; - call(res); +int prog(struct xdp_md *ctx) +{ + int id = bpf_ktime_get_ns() % 20; // We cannot use 10 here + __u64 arr[10] = {}; + for (__u32 i = 0; i < 10; ++i) { + arr[i] = i; + } + call(arr[9]); // Pass + // call(arr[10]); // Not Pass + // call(arr[11]); // Not Pass + if (id > 9 || id < 0) { // Work + goto end; + } + __u64 res = arr[id]; + call(res); #pragma nounroll - for (__u32 i = 0; i < 10; ++i) { - call(arr[i]); - arr[i] = i + 1; - } + for (__u32 i = 0; i < 10; ++i) { + call(arr[i]); + arr[i] = i + 1; + } end: - return XDP_DROP; + return XDP_DROP; } char _license[] SEC("license") = "GPL"; diff --git a/IR/tests/mem3_nop.c b/IR/tests/mem3_nop.c index 4bf4f66d..3091ffb7 100644 --- a/IR/tests/mem3_nop.c +++ b/IR/tests/mem3_nop.c @@ -1,12 +1,13 @@ #include #include -int __noinline call(__u64 i) { - if (i > 100) { - return -1; - } - bpf_trace_printk("i: %d\n", 4, i); - return 0; +int __noinline call(__u64 i) +{ + if (i > 100) { + return -1; + } + bpf_trace_printk("i: %d\n", 4, i); + return 0; } /** @@ -16,11 +17,12 @@ int __noinline call(__u64 i) { */ SEC("xdp") -int prog(struct xdp_md *ctx) { - __u64 arr[10] = {}; - call(arr[20]); // Not Pass - call(arr[11]); // Not Pass - return XDP_DROP; +int prog(struct xdp_md *ctx) +{ + __u64 arr[10] = {}; + call(arr[20]); // Not Pass + call(arr[11]); // Not Pass + return XDP_DROP; } char _license[] SEC("license") = "GPL"; diff --git a/IR/tests/test_asm.c b/IR/tests/test_asm.c index 833ff61a..9ea1d7f7 100644 --- a/IR/tests/test_asm.c +++ b/IR/tests/test_asm.c @@ -1,46 +1,51 @@ #include #include -int __noinline spill(int cr, int ci) { - int i = 0; - int zr = 0; - int zi = 0; - int zk = 0; - int zl = 0; +int __noinline spill(int cr, int ci) +{ + int i = 0; + int zr = 0; + int zi = 0; + int zk = 0; + int zl = 0; - while (i < 100 && zr * zr + zi * zi + zk * zk - zl * zl * (zi - 1) < 4) { - int t = zr * zr - zi * zi + cr; - zi = 2 * zr * zi + ci; - zr = t; - zk = 3 * zr - zi * zi * zk * zr; - zl = zl + 1; + while (i < 100 && + zr * zr + zi * zi + zk * zk - zl * zl * (zi - 1) < 4) { + int t = zr * zr - zi * zi + cr; + zi = 2 * zr * zi + ci; + zr = t; + zk = 3 * zr - zi * zi * zk * zr; + zl = zl + 1; - i = i + 1; - } - return i; + i = i + 1; + } + return i; } -void __noinline pk(char s) { - bpf_trace_printk("%c", 1, s); +void __noinline pk(char s) +{ + bpf_trace_printk("%c", 1, s); } -void __noinline pk_l(char *s) { - bpf_trace_printk("%s", 1, s); +void __noinline pk_l(char *s) +{ + bpf_trace_printk("%s", 1, s); } SEC("xdp") -int prog(void *ctx) { - int s = spill(1, 2); - static char ores[10] = "helloggg"; - static char res[10] = "helloworld"; - for (int i = 0; i < 10; ++i) { - pk(res[i]); - } - pk_l(res); - pk_l(ores); - pk(res[0]); - res[0] = s; - return 0; +int prog(void *ctx) +{ + int s = spill(1, 2); + static char ores[10] = "helloggg"; + static char res[10] = "helloworld"; + for (int i = 0; i < 10; ++i) { + pk(res[i]); + } + pk_l(res); + pk_l(ores); + pk(res[0]); + res[0] = s; + return 0; } char _license[] SEC("license") = "GPL";