aboutsummaryrefslogtreecommitdiff
path: root/gcc/config/bpf/bpf.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/bpf/bpf.c')
-rw-r--r--gcc/config/bpf/bpf.c948
1 files changed, 948 insertions, 0 deletions
diff --git a/gcc/config/bpf/bpf.c b/gcc/config/bpf/bpf.c
new file mode 100644
index 0000000..8b2a592
--- /dev/null
+++ b/gcc/config/bpf/bpf.c
@@ -0,0 +1,948 @@
+/* Subroutines used for code generation for eBPF.
+ Copyright (C) 2019 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 3, or (at your option)
+any later version.
+
+GCC is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#define IN_TARGET_CODE 1
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "tm.h"
+#include "rtl.h"
+#include "regs.h"
+#include "insn-config.h"
+#include "insn-attr.h"
+#include "recog.h"
+#include "output.h"
+#include "alias.h"
+#include "tree.h"
+#include "stringpool.h"
+#include "attribs.h"
+#include "varasm.h"
+#include "stor-layout.h"
+#include "calls.h"
+#include "function.h"
+#include "explow.h"
+#include "memmodel.h"
+#include "emit-rtl.h"
+#include "reload.h"
+#include "tm_p.h"
+#include "target.h"
+#include "target-def.h"
+#include "basic-block.h"
+#include "expr.h"
+#include "optabs.h"
+#include "bitmap.h"
+#include "df.h"
+#include "c-family/c-common.h"
+#include "diagnostic.h"
+#include "builtins.h"
+#include "predict.h"
+#include "langhooks.h"
+
+/* Per-function machine data. */
+struct GTY(()) machine_function
+{
+ /* Number of bytes saved on the stack for local variables. */
+ int local_vars_size;
+
+ /* Number of bytes saved on the stack for callee-saved
+ registers. */
+ int callee_saved_reg_size;
+};
+
+/* Data structures for the eBPF specific built-ins. */
+
+/* Maximum number of arguments taken by a builtin function, plus
+ one. */
+#define BPF_BUILTIN_MAX_ARGS 5
+
+enum bpf_builtins
+{
+ BPF_BUILTIN_UNUSED = 0,
+ /* Built-ins for kernel helpers. */
+#define DEF_HELPER(V,D,N,T) BPF_BUILTIN_HELPER_##D,
+# include "bpf-helpers.def"
+#undef DEF_HELPER
+ BPF_BUILTIN_HELPER_MAX,
+ /* Built-ins for non-generic loads and stores. */
+ BPF_BUILTIN_LOAD_BYTE = BPF_BUILTIN_HELPER_MAX,
+ BPF_BUILTIN_LOAD_HALF,
+ BPF_BUILTIN_LOAD_WORD,
+ BPF_BUILTIN_MAX,
+};
+
+/* This table is indexed by an enum bpf_builtin. */
+static const char *bpf_helper_names[] =
+{
+ NULL,
+#define DEF_HELPER(V,D,N,T) #N,
+# include "bpf-helpers.def"
+#undef DEF_HELPER
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/* Return the builtin code corresponding to the kernel helper builtin
+ __builtin_NAME, or 0 if the name doesn't correspond to a kernel
+ helper builtin. */
+
+static inline int
+bpf_helper_code (const char *name)
+{
+ int i;
+
+ for (i = 1; i < BPF_BUILTIN_HELPER_MAX; ++i)
+ if (strcmp (name, bpf_helper_names[i]) == 0)
+ return i;
+
+ return 0;
+}
+
+static GTY (()) tree bpf_builtins[(int) BPF_BUILTIN_MAX];
+
+/* Initialize the per-function machine status. */
+
+static struct machine_function *
+bpf_init_machine_status (void)
+{
+ /* Note this initializes all fields to 0, which is just OK for
+ us. */
+ return ggc_cleared_alloc<machine_function> ();
+}
+
+/* Override options and do some other initialization. */
+
+static void
+bpf_option_override (void)
+{
+ /* Set the initializer for the per-function status structure. */
+ init_machine_status = bpf_init_machine_status;
+}
+
+#undef TARGET_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE bpf_option_override
+
+/* Define target-specific CPP macros. This function in used in the
+ definition of TARGET_CPU_CPP_BUILTINS in bpf.h */
+
+#define builtin_define(TXT) cpp_define (pfile, TXT)
+
+void
+bpf_target_macros (cpp_reader *pfile)
+{
+ builtin_define ("__BPF__");
+
+ if (TARGET_BIG_ENDIAN)
+ builtin_define ("__BPF_BIG_ENDIAN__");
+ else
+ builtin_define ("__BPF_LITTLE_ENDIAN__");
+
+ /* Define BPF_KERNEL_VERSION_CODE */
+ {
+ const char *version_code;
+ char *kernel_version_code;
+
+ switch (bpf_kernel)
+ {
+ case LINUX_V4_0: version_code = "0x40000"; break;
+ case LINUX_V4_1: version_code = "0x40100"; break;
+ case LINUX_V4_2: version_code = "0x40200"; break;
+ case LINUX_V4_3: version_code = "0x40300"; break;
+ case LINUX_V4_4: version_code = "0x40400"; break;
+ case LINUX_V4_5: version_code = "0x40500"; break;
+ case LINUX_V4_6: version_code = "0x40600"; break;
+ case LINUX_V4_7: version_code = "0x40700"; break;
+ case LINUX_V4_8: version_code = "0x40800"; break;
+ case LINUX_V4_9: version_code = "0x40900"; break;
+ case LINUX_V4_10: version_code = "0x40a00"; break;
+ case LINUX_V4_11: version_code = "0x40b00"; break;
+ case LINUX_V4_12: version_code = "0x40c00"; break;
+ case LINUX_V4_13: version_code = "0x40d00"; break;
+ case LINUX_V4_14: version_code = "0x40e00"; break;
+ case LINUX_V4_15: version_code = "0x40f00"; break;
+ case LINUX_V4_16: version_code = "0x41000"; break;
+ case LINUX_V4_17: version_code = "0x42000"; break;
+ case LINUX_V4_18: version_code = "0x43000"; break;
+ case LINUX_V4_19: version_code = "0x44000"; break;
+ case LINUX_V4_20: version_code = "0x45000"; break;
+ case LINUX_V5_0: version_code = "0x50000"; break;
+ case LINUX_V5_1: version_code = "0x50100"; break;
+ case LINUX_V5_2: version_code = "0x50200"; break;
+ default:
+ gcc_unreachable ();
+ }
+
+ kernel_version_code = ACONCAT (("__BPF_KERNEL_VERSION_CODE__=",
+ version_code, NULL));
+ builtin_define (kernel_version_code);
+ }
+}
+
+/* Output assembly directives to switch to section NAME. The section
+ should have attributes as specified by FLAGS, which is a bit mask
+ of the 'SECTION_*' flags defined in 'output.h'. If DECL is
+ non-NULL, it is the 'VAR_DECL' or 'FUNCTION_DECL' with which this
+ section is associated. */
+
+static void
+bpf_asm_named_section (const char *name,
+ unsigned int flags ATTRIBUTE_UNUSED,
+ tree decl ATTRIBUTE_UNUSED)
+{
+ fprintf (asm_out_file, "\t.section\t%s\n", name);
+}
+
+#undef TARGET_ASM_NAMED_SECTION
+#define TARGET_ASM_NAMED_SECTION bpf_asm_named_section
+
+/* Return an RTX representing the place where a function returns or
+ receives a value of data type RET_TYPE, a tree node representing a
+ data type. */
+
+static rtx
+bpf_function_value (const_tree ret_type,
+ const_tree fntype_or_decl,
+ bool outgoing ATTRIBUTE_UNUSED)
+{
+ enum machine_mode mode;
+ int unsignedp;
+
+ mode = TYPE_MODE (ret_type);
+ if (INTEGRAL_TYPE_P (ret_type))
+ mode = promote_function_mode (ret_type, mode, &unsignedp,
+ fntype_or_decl, 1);
+
+ return gen_rtx_REG (mode, BPF_R0);
+}
+
+#undef TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE bpf_function_value
+
+/* Return true if REGNO is the number of a hard register in which the
+ values of called function may come back. */
+
+static bool
+bpf_function_value_regno_p (const unsigned int regno)
+{
+ return (regno == BPF_R0);
+}
+
+#undef TARGET_FUNCTION_VALUE_REGNO_P
+#define TARGET_FUNCTION_VALUE_REGNO_P bpf_function_value_regno_p
+
+/* Compute the size of the function's stack frame, including the local
+ area and the register-save area. */
+
+static void
+bpf_compute_frame_layout (void)
+{
+ int stack_alignment = STACK_BOUNDARY / BITS_PER_UNIT;
+ int padding_locals, regno;
+
+ /* Set the space used in the stack by local variables. This is
+ rounded up to respect the minimum stack alignment. */
+ cfun->machine->local_vars_size = get_frame_size ();
+
+ padding_locals = cfun->machine->local_vars_size % stack_alignment;
+ if (padding_locals)
+ padding_locals = stack_alignment - padding_locals;
+
+ cfun->machine->local_vars_size += padding_locals;
+
+ /* Set the space used in the stack by callee-saved used registers in
+ the current function. There is no need to round up, since the
+ registers are all 8 bytes wide. */
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ if ((!fixed_regs[regno]
+ && df_regs_ever_live_p (regno)
+ && !call_used_regs[regno])
+ || (cfun->calls_alloca
+ && regno == STACK_POINTER_REGNUM))
+ cfun->machine->callee_saved_reg_size += 8;
+
+ /* Check that the total size of the frame doesn't exceed the limit
+ imposed by eBPF. */
+ if ((cfun->machine->local_vars_size
+ + cfun->machine->callee_saved_reg_size) > bpf_frame_limit)
+ {
+ static int stack_limit_exceeded = 0;
+
+ if (!stack_limit_exceeded)
+ error ("eBPF stack limit exceeded");
+ stack_limit_exceeded = 1;
+ }
+}
+
+#undef TARGET_COMPUTE_FRAME_LAYOUT
+#define TARGET_COMPUTE_FRAME_LAYOUT bpf_compute_frame_layout
+
+/* Expand to the instructions in a function prologue. This function
+ is called when expanding the 'prologue' pattern in bpf.md. */
+
+void
+bpf_expand_prologue (void)
+{
+ int regno, fp_offset;
+ rtx insn;
+ HOST_WIDE_INT size;
+
+ size = (cfun->machine->local_vars_size
+ + cfun->machine->callee_saved_reg_size);
+ fp_offset = -cfun->machine->local_vars_size;
+
+ /* Save callee-saved hard registes. The register-save-area starts
+ right after the local variables. */
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ {
+ if ((!fixed_regs[regno]
+ && df_regs_ever_live_p (regno)
+ && !call_used_regs[regno])
+ || (cfun->calls_alloca
+ && regno == STACK_POINTER_REGNUM))
+ {
+ rtx mem;
+
+ if (!IN_RANGE (fp_offset, -1 - 0x7fff, 0x7fff))
+ /* This has been already reported as an error in
+ bpf_compute_frame_layout. */
+ break;
+ else
+ {
+ mem = gen_frame_mem (DImode,
+ plus_constant (DImode,
+ hard_frame_pointer_rtx,
+ fp_offset - 8));
+ insn = emit_move_insn (mem, gen_rtx_REG (DImode, regno));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ fp_offset -= 8;
+ }
+ }
+ }
+
+ /* Set the stack pointer, if the function allocates space
+ dynamically. Note that the value of %sp should be directly
+ derived from %fp, for the kernel verifier to track it as a stack
+ accessor. */
+ if (cfun->calls_alloca)
+ {
+ insn = emit_move_insn (stack_pointer_rtx,
+ hard_frame_pointer_rtx);
+ RTX_FRAME_RELATED_P (insn) = 1;
+
+ if (size > 0)
+ {
+ insn = emit_insn (gen_rtx_SET (stack_pointer_rtx,
+ gen_rtx_PLUS (Pmode,
+ stack_pointer_rtx,
+ GEN_INT (-size))));
+ RTX_FRAME_RELATED_P (insn) = 1;
+ }
+ }
+}
+
+/* Expand to the instructions in a function epilogue. This function
+ is called when expanding the 'epilogue' pattern in bpf.md. */
+
+void
+bpf_expand_epilogue (void)
+{
+ int regno, fp_offset;
+ rtx insn;
+
+ fp_offset = -cfun->machine->local_vars_size;
+
+ /* Restore callee-saved hard registes from the stack. */
+ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++)
+ {
+ if ((!fixed_regs[regno]
+ && df_regs_ever_live_p (regno)
+ && !call_used_regs[regno])
+ || (cfun->calls_alloca
+ && regno == STACK_POINTER_REGNUM))
+ {
+ rtx mem;
+
+ if (!IN_RANGE (fp_offset, -1 - 0x7fff, 0x7fff))
+ /* This has been already reported as an error in
+ bpf_compute_frame_layout. */
+ break;
+ else
+ {
+ mem = gen_frame_mem (DImode,
+ plus_constant (DImode,
+ hard_frame_pointer_rtx,
+ fp_offset - 8));
+ insn = emit_move_insn (gen_rtx_REG (DImode, regno), mem);
+ RTX_FRAME_RELATED_P (insn) = 1;
+ fp_offset -= 8;
+ }
+ }
+ }
+
+ emit_jump_insn (gen_exit ());
+}
+
+/* Return the initial difference between the specified pair of
+ registers. The registers that can figure in FROM, and TO, are
+ specified by ELIMINABLE_REGS in bpf.h.
+
+ This function is used in the definition of
+ INITIAL_ELIMINATION_OFFSET in bpf.h */
+
+HOST_WIDE_INT
+bpf_initial_elimination_offset (int from, int to)
+{
+ HOST_WIDE_INT ret;
+
+ if (from == ARG_POINTER_REGNUM && to == STACK_POINTER_REGNUM)
+ ret = (cfun->machine->local_vars_size
+ + cfun->machine->callee_saved_reg_size);
+ else if (from == ARG_POINTER_REGNUM && to == FRAME_POINTER_REGNUM)
+ ret = 0;
+ else
+ gcc_unreachable ();
+
+ return ret;
+}
+
+/* Return the number of consecutive hard registers, starting at
+ register number REGNO, required to hold a value of mode MODE. */
+
+static unsigned int
+bpf_hard_regno_nregs (unsigned int regno ATTRIBUTE_UNUSED,
+ enum machine_mode mode)
+{
+ return CEIL (GET_MODE_SIZE (mode), UNITS_PER_WORD);
+}
+
+#undef TARGET_HARD_REGNO_NREGS
+#define TARGET_HARD_REGNO_NREGS bpf_hard_regno_nregs
+
+/* Return true if it is permissible to store a value of mode MODE in
+ hard register number REGNO, or in several registers starting with
+ that one. */
+
+static bool
+bpf_hard_regno_mode_ok (unsigned int regno ATTRIBUTE_UNUSED,
+ enum machine_mode mode)
+{
+ switch (mode)
+ {
+ case E_SImode:
+ case E_DImode:
+ case E_HImode:
+ case E_QImode:
+ case E_TImode:
+ case E_SFmode:
+ case E_DFmode:
+ return true;
+ default:
+ return false;
+ }
+}
+
+#undef TARGET_HARD_REGNO_MODE_OK
+#define TARGET_HARD_REGNO_MODE_OK bpf_hard_regno_mode_ok
+
+/* Return true if a function must have and use a frame pointer. */
+
+static bool
+bpf_frame_pointer_required (void)
+{
+ /* We do not have a stack pointer, so we absolutely depend on the
+ frame-pointer in order to access the stack... and fishes walk and
+ pigs fly glglgl */
+ return true;
+}
+
+#undef TARGET_FRAME_POINTER_REQUIRED
+#define TARGET_FRAME_POINTER_REQUIRED bpf_frame_pointer_required
+
+/* Return `true' if the given RTX X is a valid base for an indirect
+ memory access. STRICT has the same meaning than in
+ bpf_legitimate_address_p. */
+
+static inline bool
+bpf_address_base_p (rtx x, bool strict)
+{
+ return (GET_CODE (x) == REG
+ && (REGNO (x) < 11
+ || (!strict && REGNO (x) >= FIRST_PSEUDO_REGISTER)));
+}
+
+/* Return true if X (a RTX) is a legitimate memory address on the
+ target machine for a memory operand of mode MODE. */
+
+static bool
+bpf_legitimate_address_p (machine_mode mode ATTRIBUTE_UNUSED,
+ rtx x,
+ bool strict)
+{
+ switch (GET_CODE (x))
+ {
+ case REG:
+ return bpf_address_base_p (x, strict);
+
+ case PLUS:
+ {
+ /* Accept (PLUS ADDR_BASE CONST_INT), provided CONST_INT fits
+ in a signed 16-bit.
+
+ Note that LABEL_REF and SYMBOL_REF are not allowed in
+ REG+IMM addresses, because it is almost certain they will
+ overload the offset field. */
+
+ rtx x0 = XEXP (x, 0);
+ rtx x1 = XEXP (x, 1);
+
+ if (bpf_address_base_p (x0, strict) && GET_CODE (x1) == CONST_INT)
+ return IN_RANGE (INTVAL (x1), -1 - 0x7fff, 0x7fff);
+
+ break;
+ }
+ default:
+ break;
+ }
+
+ return false;
+}
+
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P bpf_legitimate_address_p
+
+/* Describe the relative costs of RTL expressions. Return true when
+ all subexpressions of X have been processed, and false when
+ `rtx_cost' should recurse. */
+
+static bool
+bpf_rtx_costs (rtx x ATTRIBUTE_UNUSED,
+ enum machine_mode mode ATTRIBUTE_UNUSED,
+ int outer_code ATTRIBUTE_UNUSED,
+ int opno ATTRIBUTE_UNUSED,
+ int *total ATTRIBUTE_UNUSED,
+ bool speed ATTRIBUTE_UNUSED)
+{
+ /* To be written. */
+ return false;
+}
+
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS bpf_rtx_costs
+
+/* Return true if an argument at the position indicated by CUM should
+ be passed by reference. If the hook returns true, a copy of that
+ argument is made in memory and a pointer to the argument is passed
+ instead of the argument itself. */
+
+static bool
+bpf_pass_by_reference (cumulative_args_t cum ATTRIBUTE_UNUSED,
+ const function_arg_info &arg)
+{
+ unsigned num_bytes = arg.type_size_in_bytes ();
+
+ /* Pass aggregates and values bigger than 5 words by reference.
+ Everything else is passed by copy. */
+ return (arg.aggregate_type_p () || (num_bytes > 8*5));
+}
+
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE bpf_pass_by_reference
+
+/* Return a RTX indicating whether a function argument is passed in a
+ register and if so, which register. */
+
+static rtx
+bpf_function_arg (cumulative_args_t ca, const function_arg_info &arg)
+{
+ CUMULATIVE_ARGS *cum = get_cumulative_args (ca);
+
+ if (*cum < 5)
+ return gen_rtx_REG (arg.mode, *cum + 1);
+ else
+ /* An error will be emitted for this in
+ bpf_function_arg_advance. */
+ return NULL_RTX;
+}
+
+#undef TARGET_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG bpf_function_arg
+
+/* Update the summarizer variable pointed by CA to advance past an
+ argument in the argument list. */
+
+static void
+bpf_function_arg_advance (cumulative_args_t ca,
+ const function_arg_info &arg)
+{
+ CUMULATIVE_ARGS *cum = get_cumulative_args (ca);
+ unsigned num_bytes = arg.type_size_in_bytes ();
+ unsigned num_words = CEIL (num_bytes, UNITS_PER_WORD);
+
+ if (*cum <= 5 && *cum + num_words > 5)
+ error ("too many function arguments for eBPF");
+
+ *cum += num_words;
+}
+
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE bpf_function_arg_advance
+
+/* Output the assembly code for a constructor. Since eBPF doesn't
+ support indirect calls, constructors are not supported. */
+
+static void
+bpf_output_constructor (rtx symbol, int priority ATTRIBUTE_UNUSED)
+{
+ tree decl = SYMBOL_REF_DECL (symbol);
+
+ if (decl)
+ sorry_at (DECL_SOURCE_LOCATION (decl),
+ "no constructors");
+ else
+ sorry ("no constructors");
+}
+
+#undef TARGET_ASM_CONSTRUCTOR
+#define TARGET_ASM_CONSTRUCTOR bpf_output_constructor
+
+/* Output the assembly code for a destructor. Since eBPF doesn't
+ support indirect calls, destructors are not supported. */
+
+static void
+bpf_output_destructor (rtx symbol, int priority ATTRIBUTE_UNUSED)
+{
+ tree decl = SYMBOL_REF_DECL (symbol);
+
+ if (decl)
+ sorry_at (DECL_SOURCE_LOCATION (decl),
+ "no destructors");
+ else
+ sorry ("no destructors");
+}
+
+#undef TARGET_ASM_DESTRUCTOR
+#define TARGET_ASM_DESTRUCTOR bpf_output_destructor
+
+/* Return the appropriate instruction to CALL to a function. TARGET
+ is an RTX denoting the address of the called function.
+
+ The main purposes of this function are:
+ - To reject indirect CALL instructions, which are not supported by
+ eBPF.
+ - To recognize calls to kernel helper functions and emit the
+ corresponding CALL N instruction.
+
+ This function is called from the expansion of the 'call' pattern in
+ bpf.md. */
+
+const char *
+bpf_output_call (rtx target)
+{
+ rtx xops[1];
+
+ switch (GET_CODE (target))
+ {
+ case CONST_INT:
+ output_asm_insn ("call\t%0", &target);
+ break;
+ case SYMBOL_REF:
+ {
+ const char *function_name = XSTR (target, 0);
+ int code;
+
+ if (strncmp (function_name, "__builtin_bpf_helper_", 21) == 0
+ && ((code = bpf_helper_code (function_name + 21)) != 0))
+ {
+ xops[0] = GEN_INT (code);
+ output_asm_insn ("call\t%0", xops);
+ }
+ else
+ output_asm_insn ("call\t%0", &target);
+
+ break;
+ }
+ default:
+ error ("indirect call in function, which are not supported by eBPF");
+ output_asm_insn ("call 0", NULL);
+ break;
+ }
+
+ return "";
+}
+
+/* Print an instruction operand. This function is called in the macro
+ PRINT_OPERAND defined in bpf.h */
+
+void
+bpf_print_operand (FILE *file, rtx op, int code ATTRIBUTE_UNUSED)
+{
+ switch (GET_CODE (op))
+ {
+ case REG:
+ fprintf (file, "%s", reg_names[REGNO (op)]);
+ break;
+ case MEM:
+ output_address (GET_MODE (op), XEXP (op, 0));
+ break;
+ case CONST_DOUBLE:
+ if (CONST_DOUBLE_HIGH (op))
+ fprintf (file, HOST_WIDE_INT_PRINT_DOUBLE_HEX,
+ CONST_DOUBLE_HIGH (op), CONST_DOUBLE_LOW (op));
+ else if (CONST_DOUBLE_LOW (op) < 0)
+ fprintf (file, HOST_WIDE_INT_PRINT_HEX, CONST_DOUBLE_LOW (op));
+ else
+ fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (op));
+ break;
+ default:
+ output_addr_const (file, op);
+ }
+}
+
+/* Print an operand which is an address. This function should handle
+ any legit address, as accepted by bpf_legitimate_address_p, and
+ also addresses that are valid in CALL instructions.
+
+ This function is called in the PRINT_OPERAND_ADDRESS macro defined
+ in bpf.h */
+
+void
+bpf_print_operand_address (FILE *file, rtx addr)
+{
+ switch (GET_CODE (addr))
+ {
+ case REG:
+ fprintf (file, "[%s+0]", reg_names[REGNO (addr)]);
+ break;
+ case PLUS:
+ {
+ rtx op0 = XEXP (addr, 0);
+ rtx op1 = XEXP (addr, 1);
+
+ if (GET_CODE (op0) == REG && GET_CODE (op1) == CONST_INT)
+ {
+ fprintf (file, "[%s+", reg_names[REGNO (op0)]);
+ output_addr_const (file, op1);
+ fputs ("]", file);
+ }
+ else
+ fatal_insn ("invalid address in operand", addr);
+ break;
+ }
+ case MEM:
+ /* Fallthrough. */
+ case LABEL_REF:
+ /* Fallthrough. */
+ fatal_insn ("unsupported operand", addr);
+ break;
+ default:
+ output_addr_const (file, addr);
+ break;
+ }
+}
+
+/* Add a BPF builtin function with NAME, CODE and TYPE. Return
+ the function decl or NULL_TREE if the builtin was not added. */
+
+static tree
+def_builtin (const char *name, enum bpf_builtins code, tree type)
+{
+ tree t
+ = add_builtin_function (name, type, code, BUILT_IN_MD, NULL, NULL_TREE);
+
+ bpf_builtins[code] = t;
+ return t;
+}
+
+/* Define machine-specific built-in functions. */
+
+static void
+bpf_init_builtins (void)
+{
+ /* Built-ins for calling kernel helpers. */
+
+ tree pt = build_pointer_type (void_type_node);
+ tree const_void_type
+ = build_qualified_type (void_type_node, TYPE_QUAL_CONST);
+ tree cpt = build_pointer_type (const_void_type);
+ tree st = short_integer_type_node;
+ tree ust = uint16_type_node;
+ tree it = integer_type_node;
+ tree ut = unsigned_type_node;
+ tree const_char_type
+ = build_qualified_type (char_type_node, TYPE_QUAL_CONST);
+ tree cst = build_pointer_type (const_char_type);
+ tree vt = void_type_node;
+ tree ult = long_unsigned_type_node;
+ tree u32t = uint32_type_node;
+ tree u64t = uint64_type_node;
+ tree llt = long_long_integer_type_node;
+ tree ullt = long_long_unsigned_type_node;
+
+#define TYPES build_function_type_list
+#define VTYPES build_varargs_function_type_list
+#define DEF_HELPER(V,D,N,T) \
+ do \
+ { \
+ if (bpf_kernel >= (V)) \
+ def_builtin ("__builtin_bpf_helper_" #N, \
+ BPF_BUILTIN_HELPER_##D, \
+ T); \
+ } while (0);
+# include "bpf-helpers.def"
+#undef TYPES
+#undef VTYPES
+#undef DEF_HELPER
+
+ /* Built-ins for BPF_LD_ABS and BPF_LD_IND instructions. */
+
+ def_builtin ("__builtin_bpf_load_byte", BPF_BUILTIN_LOAD_BYTE,
+ build_function_type_list (ullt, ullt, 0));
+ def_builtin ("__builtin_bpf_load_half", BPF_BUILTIN_LOAD_HALF,
+ build_function_type_list (ullt, ullt, 0));
+ def_builtin ("__builtin_bpf_load_word", BPF_BUILTIN_LOAD_WORD,
+ build_function_type_list (ullt, ullt, 0));
+}
+
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS bpf_init_builtins
+
+/* Expand a call to a BPF-specific built-in function that was set up
+ with bpf_init_builtins. */
+
+static rtx
+bpf_expand_builtin (tree exp, rtx target,
+ rtx subtarget ATTRIBUTE_UNUSED,
+ machine_mode mode ATTRIBUTE_UNUSED,
+ int ignore)
+{
+ tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
+ int code = DECL_MD_FUNCTION_CODE (fndecl);
+
+ if (code >= 1 && code < BPF_BUILTIN_HELPER_MAX)
+ {
+ /* This is a builtin to call a kernel helper function.
+
+ For these builtins, we just expand the function call normally
+ with expand_call like we would do for a libcall. The function
+ bpf_output_call below will then do The Right Thing (TM),
+ recognizing the name of the called __builtin_helper_* symbol
+ and emitting the corresponding CALL N instruction whenever
+ necessary. */
+
+ return expand_call (exp, target, ignore);
+ }
+ else if (code == BPF_BUILTIN_LOAD_BYTE
+ || code == BPF_BUILTIN_LOAD_HALF
+ || code == BPF_BUILTIN_LOAD_WORD)
+ {
+ /* Expand an indirect load from the sk_buff in the context.
+ There is just one argument to the builtin, which is the
+ offset.
+
+ We try first to expand a ldabs* instruction. In case this
+ fails, we try a ldind* instruction. */
+
+ enum insn_code abs_icode
+ = (code == BPF_BUILTIN_LOAD_BYTE ? CODE_FOR_ldabsb
+ : code == BPF_BUILTIN_LOAD_HALF ? CODE_FOR_ldabsh
+ : CODE_FOR_ldabsw);
+
+ enum insn_code ind_icode
+ = (code == BPF_BUILTIN_LOAD_BYTE ? CODE_FOR_ldindb
+ : code == BPF_BUILTIN_LOAD_HALF ? CODE_FOR_ldindh
+ : CODE_FOR_ldindw);
+
+ tree offset_arg = CALL_EXPR_ARG (exp, 0);
+ struct expand_operand ops[2];
+
+ create_input_operand (&ops[0], expand_normal (offset_arg),
+ TYPE_MODE (TREE_TYPE (offset_arg)));
+ create_input_operand (&ops[1], const0_rtx, SImode);
+
+ if (!maybe_expand_insn (abs_icode, 2, ops)
+ && !maybe_expand_insn (ind_icode, 2, ops))
+ {
+ error ("invalid argument to built-in function");
+ return gen_rtx_REG (ops[0].mode, BPF_R0);
+ }
+
+ /* The result of the load is in R0. */
+ return gen_rtx_REG (ops[0].mode, BPF_R0);
+ }
+
+ gcc_unreachable ();
+}
+
+#undef TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN bpf_expand_builtin
+
+/* Initialize target-specific function library calls. This is mainly
+ used to call library-provided soft-fp operations, since eBPF
+ doesn't support floating-point in "hardware". */
+
+static void
+bpf_init_libfuncs (void)
+{
+ set_conv_libfunc (sext_optab, DFmode, SFmode,
+ "__bpf_extendsfdf2");
+ set_conv_libfunc (trunc_optab, SFmode, DFmode,
+ "__bpf_truncdfsf2");
+ set_conv_libfunc (sfix_optab, SImode, DFmode,
+ "__bpf_fix_truncdfsi");
+ set_conv_libfunc (sfloat_optab, DFmode, SImode,
+ "__bpf_floatsidf");
+ set_conv_libfunc (ufloat_optab, DFmode, SImode,
+ "__bpf_floatunsidf");
+}
+
+#undef TARGET_INIT_LIBFUNCS
+#define TARGET_INIT_LIBFUNCS bpf_init_libfuncs
+
+/* Define the mechanism that will be used for describing frame unwind
+ information to the debugger. In eBPF it is not possible to unwind
+ frames. */
+
+static enum unwind_info_type
+bpf_debug_unwind_info ()
+{
+ return UI_NONE;
+}
+
+#undef TARGET_DEBUG_UNWIND_INFO
+#define TARGET_DEBUG_UNWIND_INFO bpf_debug_unwind_info
+
+/* Output assembly directives to assemble data of various sized and
+ alignments. */
+
+#undef TARGET_ASM_BYTE_OP
+#define TARGET_ASM_BYTE_OP "\t.byte\t"
+#undef TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\t.half\t"
+#undef TARGET_ASM_ALIGNED_SI_OP
+#define TARGET_ASM_ALIGNED_SI_OP "\t.word\t"
+#undef TARGET_ASM_ALIGNED_DI_OP
+#define TARGET_ASM_ALIGNED_DI_OP "\t.dword\t"
+
+/* Finally, build the GCC target. */
+
+struct gcc_target targetm = TARGET_INITIALIZER;
+
+#include "gt-bpf.h"