;; Machine description for ARM processor synchronization primitives. ;; Copyright (C) 2010 Free Software Foundation, Inc. ;; Written by Marcus Shawcroft (marcus.shawcroft@arm.com) ;; ;; 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 ;; . */ ;; ARMV6 introduced ldrex and strex instruction. These instruction ;; access SI width data. In order to implement synchronization ;; primitives for the narrower QI and HI modes we insert appropriate ;; AND/OR sequences into the synchronization loop to mask out the ;; relevant component of an SI access. (define_expand "memory_barrier" [(set (match_dup 0) (unspec:BLK [(match_dup 0)] UNSPEC_MEMORY_BARRIER))] "TARGET_HAVE_MEMORY_BARRIER" { operands[0] = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (Pmode)); MEM_VOLATILE_P (operands[0]) = 1; }) (define_expand "sync_compare_and_swapsi" [(set (match_operand:SI 0 "s_register_operand") (unspec_volatile:SI [(match_operand:SI 1 "memory_operand") (match_operand:SI 2 "s_register_operand") (match_operand:SI 3 "s_register_operand")] VUNSPEC_SYNC_COMPARE_AND_SWAP))] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omrn; generator.u.omrn = gen_arm_sync_compare_and_swapsi; arm_expand_sync (SImode, &generator, operands[0], operands[1], operands[2], operands[3]); DONE; }) (define_mode_iterator NARROW [QI HI]) (define_expand "sync_compare_and_swap" [(set (match_operand:NARROW 0 "s_register_operand") (unspec_volatile:NARROW [(match_operand:NARROW 1 "memory_operand") (match_operand:NARROW 2 "s_register_operand") (match_operand:NARROW 3 "s_register_operand")] VUNSPEC_SYNC_COMPARE_AND_SWAP))] "TARGET_HAVE_LDREXBHD && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omrn; generator.u.omrn = gen_arm_sync_compare_and_swap; arm_expand_sync (mode, &generator, operands[0], operands[1], operands[2], operands[3]); DONE; }) (define_expand "sync_lock_test_and_setsi" [(match_operand:SI 0 "s_register_operand") (match_operand:SI 1 "memory_operand") (match_operand:SI 2 "s_register_operand")] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omn; generator.u.omn = gen_arm_sync_lock_test_and_setsi; arm_expand_sync (SImode, &generator, operands[0], operands[1], NULL, operands[2]); DONE; }) (define_expand "sync_lock_test_and_set" [(match_operand:NARROW 0 "s_register_operand") (match_operand:NARROW 1 "memory_operand") (match_operand:NARROW 2 "s_register_operand")] "TARGET_HAVE_LDREXBHD && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omn; generator.u.omn = gen_arm_sync_lock_test_and_set; arm_expand_sync (mode, &generator, operands[0], operands[1], NULL, operands[2]); DONE; }) (define_code_iterator syncop [plus minus ior xor and]) (define_code_attr sync_optab [(ior "ior") (xor "xor") (and "and") (plus "add") (minus "sub")]) (define_code_attr sync_clobber [(ior "=&r") (and "=&r") (xor "X") (plus "X") (minus "X")]) (define_code_attr sync_t2_reqd [(ior "4") (and "4") (xor "*") (plus "*") (minus "*")]) (define_expand "sync_si" [(match_operand:SI 0 "memory_operand") (match_operand:SI 1 "s_register_operand") (syncop:SI (match_dup 0) (match_dup 1))] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omn; generator.u.omn = gen_arm_sync_new_si; arm_expand_sync (SImode, &generator, NULL, operands[0], NULL, operands[1]); DONE; }) (define_expand "sync_nandsi" [(match_operand:SI 0 "memory_operand") (match_operand:SI 1 "s_register_operand") (not:SI (and:SI (match_dup 0) (match_dup 1)))] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omn; generator.u.omn = gen_arm_sync_new_nandsi; arm_expand_sync (SImode, &generator, NULL, operands[0], NULL, operands[1]); DONE; }) (define_expand "sync_" [(match_operand:NARROW 0 "memory_operand") (match_operand:NARROW 1 "s_register_operand") (syncop:NARROW (match_dup 0) (match_dup 1))] "TARGET_HAVE_LDREXBHD && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omn; generator.u.omn = gen_arm_sync_new_; arm_expand_sync (mode, &generator, NULL, operands[0], NULL, operands[1]); DONE; }) (define_expand "sync_nand" [(match_operand:NARROW 0 "memory_operand") (match_operand:NARROW 1 "s_register_operand") (not:NARROW (and:NARROW (match_dup 0) (match_dup 1)))] "TARGET_HAVE_LDREXBHD && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omn; generator.u.omn = gen_arm_sync_new_nand; arm_expand_sync (mode, &generator, NULL, operands[0], NULL, operands[1]); DONE; }) (define_expand "sync_new_si" [(match_operand:SI 0 "s_register_operand") (match_operand:SI 1 "memory_operand") (match_operand:SI 2 "s_register_operand") (syncop:SI (match_dup 1) (match_dup 2))] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omn; generator.u.omn = gen_arm_sync_new_si; arm_expand_sync (SImode, &generator, operands[0], operands[1], NULL, operands[2]); DONE; }) (define_expand "sync_new_nandsi" [(match_operand:SI 0 "s_register_operand") (match_operand:SI 1 "memory_operand") (match_operand:SI 2 "s_register_operand") (not:SI (and:SI (match_dup 1) (match_dup 2)))] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omn; generator.u.omn = gen_arm_sync_new_nandsi; arm_expand_sync (SImode, &generator, operands[0], operands[1], NULL, operands[2]); DONE; }) (define_expand "sync_new_" [(match_operand:NARROW 0 "s_register_operand") (match_operand:NARROW 1 "memory_operand") (match_operand:NARROW 2 "s_register_operand") (syncop:NARROW (match_dup 1) (match_dup 2))] "TARGET_HAVE_LDREXBHD && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omn; generator.u.omn = gen_arm_sync_new_; arm_expand_sync (mode, &generator, operands[0], operands[1], NULL, operands[2]); DONE; }) (define_expand "sync_new_nand" [(match_operand:NARROW 0 "s_register_operand") (match_operand:NARROW 1 "memory_operand") (match_operand:NARROW 2 "s_register_operand") (not:NARROW (and:NARROW (match_dup 1) (match_dup 2)))] "TARGET_HAVE_LDREXBHD && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omn; generator.u.omn = gen_arm_sync_new_nand; arm_expand_sync (mode, &generator, operands[0], operands[1], NULL, operands[2]); DONE; }); (define_expand "sync_old_si" [(match_operand:SI 0 "s_register_operand") (match_operand:SI 1 "memory_operand") (match_operand:SI 2 "s_register_operand") (syncop:SI (match_dup 1) (match_dup 2))] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omn; generator.u.omn = gen_arm_sync_old_si; arm_expand_sync (SImode, &generator, operands[0], operands[1], NULL, operands[2]); DONE; }) (define_expand "sync_old_nandsi" [(match_operand:SI 0 "s_register_operand") (match_operand:SI 1 "memory_operand") (match_operand:SI 2 "s_register_operand") (not:SI (and:SI (match_dup 1) (match_dup 2)))] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omn; generator.u.omn = gen_arm_sync_old_nandsi; arm_expand_sync (SImode, &generator, operands[0], operands[1], NULL, operands[2]); DONE; }) (define_expand "sync_old_" [(match_operand:NARROW 0 "s_register_operand") (match_operand:NARROW 1 "memory_operand") (match_operand:NARROW 2 "s_register_operand") (syncop:NARROW (match_dup 1) (match_dup 2))] "TARGET_HAVE_LDREXBHD && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omn; generator.u.omn = gen_arm_sync_old_; arm_expand_sync (mode, &generator, operands[0], operands[1], NULL, operands[2]); DONE; }) (define_expand "sync_old_nand" [(match_operand:NARROW 0 "s_register_operand") (match_operand:NARROW 1 "memory_operand") (match_operand:NARROW 2 "s_register_operand") (not:NARROW (and:NARROW (match_dup 1) (match_dup 2)))] "TARGET_HAVE_LDREXBHD && TARGET_HAVE_MEMORY_BARRIER" { struct arm_sync_generator generator; generator.op = arm_sync_generator_omn; generator.u.omn = gen_arm_sync_old_nand; arm_expand_sync (mode, &generator, operands[0], operands[1], NULL, operands[2]); DONE; }) (define_insn "arm_sync_compare_and_swapsi" [(set (match_operand:SI 0 "s_register_operand" "=&r") (unspec_volatile:SI [(match_operand:SI 1 "arm_sync_memory_operand" "+Q") (match_operand:SI 2 "s_register_operand" "r") (match_operand:SI 3 "s_register_operand" "r")] VUNSPEC_SYNC_COMPARE_AND_SWAP)) (set (match_dup 1) (unspec_volatile:SI [(match_dup 2)] VUNSPEC_SYNC_COMPARE_AND_SWAP)) (set (reg:CC CC_REGNUM) (unspec_volatile:CC [(match_dup 1)] VUNSPEC_SYNC_COMPARE_AND_SWAP)) ] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { return arm_output_sync_insn (insn, operands); } [(set_attr "sync_result" "0") (set_attr "sync_memory" "1") (set_attr "sync_required_value" "2") (set_attr "sync_new_value" "3") (set_attr "sync_t1" "0") (set_attr "conds" "clob") (set_attr "predicable" "no")]) (define_insn "arm_sync_compare_and_swap" [(set (match_operand:SI 0 "s_register_operand" "=&r") (zero_extend:SI (unspec_volatile:NARROW [(match_operand:NARROW 1 "arm_sync_memory_operand" "+Q") (match_operand:SI 2 "s_register_operand" "r") (match_operand:SI 3 "s_register_operand" "r")] VUNSPEC_SYNC_COMPARE_AND_SWAP))) (set (match_dup 1) (unspec_volatile:NARROW [(match_dup 2)] VUNSPEC_SYNC_COMPARE_AND_SWAP)) (set (reg:CC CC_REGNUM) (unspec_volatile:CC [(match_dup 1)] VUNSPEC_SYNC_COMPARE_AND_SWAP)) ] "TARGET_HAVE_LDREXBHD && TARGET_HAVE_MEMORY_BARRIER" { return arm_output_sync_insn (insn, operands); } [(set_attr "sync_result" "0") (set_attr "sync_memory" "1") (set_attr "sync_required_value" "2") (set_attr "sync_new_value" "3") (set_attr "sync_t1" "0") (set_attr "conds" "clob") (set_attr "predicable" "no")]) (define_insn "arm_sync_lock_test_and_setsi" [(set (match_operand:SI 0 "s_register_operand" "=&r") (match_operand:SI 1 "arm_sync_memory_operand" "+Q")) (set (match_dup 1) (unspec_volatile:SI [(match_operand:SI 2 "s_register_operand" "r")] VUNSPEC_SYNC_LOCK)) (clobber (reg:CC CC_REGNUM)) (clobber (match_scratch:SI 3 "=&r"))] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { return arm_output_sync_insn (insn, operands); } [(set_attr "sync_release_barrier" "no") (set_attr "sync_result" "0") (set_attr "sync_memory" "1") (set_attr "sync_new_value" "2") (set_attr "sync_t1" "0") (set_attr "sync_t2" "3") (set_attr "conds" "clob") (set_attr "predicable" "no")]) (define_insn "arm_sync_lock_test_and_set" [(set (match_operand:SI 0 "s_register_operand" "=&r") (zero_extend:SI (match_operand:NARROW 1 "arm_sync_memory_operand" "+Q"))) (set (match_dup 1) (unspec_volatile:NARROW [(match_operand:SI 2 "s_register_operand" "r")] VUNSPEC_SYNC_LOCK)) (clobber (reg:CC CC_REGNUM)) (clobber (match_scratch:SI 3 "=&r"))] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { return arm_output_sync_insn (insn, operands); } [(set_attr "sync_release_barrier" "no") (set_attr "sync_result" "0") (set_attr "sync_memory" "1") (set_attr "sync_new_value" "2") (set_attr "sync_t1" "0") (set_attr "sync_t2" "3") (set_attr "conds" "clob") (set_attr "predicable" "no")]) (define_insn "arm_sync_new_si" [(set (match_operand:SI 0 "s_register_operand" "=&r") (unspec_volatile:SI [(syncop:SI (match_operand:SI 1 "arm_sync_memory_operand" "+Q") (match_operand:SI 2 "s_register_operand" "r")) ] VUNSPEC_SYNC_NEW_OP)) (set (match_dup 1) (unspec_volatile:SI [(match_dup 1) (match_dup 2)] VUNSPEC_SYNC_NEW_OP)) (clobber (reg:CC CC_REGNUM)) (clobber (match_scratch:SI 3 "=&r"))] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { return arm_output_sync_insn (insn, operands); } [(set_attr "sync_result" "0") (set_attr "sync_memory" "1") (set_attr "sync_new_value" "2") (set_attr "sync_t1" "0") (set_attr "sync_t2" "3") (set_attr "sync_op" "") (set_attr "conds" "clob") (set_attr "predicable" "no")]) (define_insn "arm_sync_new_nandsi" [(set (match_operand:SI 0 "s_register_operand" "=&r") (unspec_volatile:SI [(not:SI (and:SI (match_operand:SI 1 "arm_sync_memory_operand" "+Q") (match_operand:SI 2 "s_register_operand" "r"))) ] VUNSPEC_SYNC_NEW_OP)) (set (match_dup 1) (unspec_volatile:SI [(match_dup 1) (match_dup 2)] VUNSPEC_SYNC_NEW_OP)) (clobber (reg:CC CC_REGNUM)) (clobber (match_scratch:SI 3 "=&r"))] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { return arm_output_sync_insn (insn, operands); } [(set_attr "sync_result" "0") (set_attr "sync_memory" "1") (set_attr "sync_new_value" "2") (set_attr "sync_t1" "0") (set_attr "sync_t2" "3") (set_attr "sync_op" "nand") (set_attr "conds" "clob") (set_attr "predicable" "no")]) (define_insn "arm_sync_new_" [(set (match_operand:SI 0 "s_register_operand" "=&r") (unspec_volatile:SI [(syncop:SI (zero_extend:SI (match_operand:NARROW 1 "arm_sync_memory_operand" "+Q")) (match_operand:SI 2 "s_register_operand" "r")) ] VUNSPEC_SYNC_NEW_OP)) (set (match_dup 1) (unspec_volatile:NARROW [(match_dup 1) (match_dup 2)] VUNSPEC_SYNC_NEW_OP)) (clobber (reg:CC CC_REGNUM)) (clobber (match_scratch:SI 3 "=&r"))] "TARGET_HAVE_LDREXBHD && TARGET_HAVE_MEMORY_BARRIER" { return arm_output_sync_insn (insn, operands); } [(set_attr "sync_result" "0") (set_attr "sync_memory" "1") (set_attr "sync_new_value" "2") (set_attr "sync_t1" "0") (set_attr "sync_t2" "3") (set_attr "sync_op" "") (set_attr "conds" "clob") (set_attr "predicable" "no")]) (define_insn "arm_sync_new_nand" [(set (match_operand:SI 0 "s_register_operand" "=&r") (unspec_volatile:SI [(not:SI (and:SI (zero_extend:SI (match_operand:NARROW 1 "arm_sync_memory_operand" "+Q")) (match_operand:SI 2 "s_register_operand" "r"))) ] VUNSPEC_SYNC_NEW_OP)) (set (match_dup 1) (unspec_volatile:NARROW [(match_dup 1) (match_dup 2)] VUNSPEC_SYNC_NEW_OP)) (clobber (reg:CC CC_REGNUM)) (clobber (match_scratch:SI 3 "=&r"))] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { return arm_output_sync_insn (insn, operands); } [(set_attr "sync_result" "0") (set_attr "sync_memory" "1") (set_attr "sync_new_value" "2") (set_attr "sync_t1" "0") (set_attr "sync_t2" "3") (set_attr "sync_op" "nand") (set_attr "conds" "clob") (set_attr "predicable" "no")]) (define_insn "arm_sync_old_si" [(set (match_operand:SI 0 "s_register_operand" "=&r") (unspec_volatile:SI [(syncop:SI (match_operand:SI 1 "arm_sync_memory_operand" "+Q") (match_operand:SI 2 "s_register_operand" "r")) ] VUNSPEC_SYNC_OLD_OP)) (set (match_dup 1) (unspec_volatile:SI [(match_dup 1) (match_dup 2)] VUNSPEC_SYNC_OLD_OP)) (clobber (reg:CC CC_REGNUM)) (clobber (match_scratch:SI 3 "=&r")) (clobber (match_scratch:SI 4 ""))] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { return arm_output_sync_insn (insn, operands); } [(set_attr "sync_result" "0") (set_attr "sync_memory" "1") (set_attr "sync_new_value" "2") (set_attr "sync_t1" "3") (set_attr "sync_t2" "") (set_attr "sync_op" "") (set_attr "conds" "clob") (set_attr "predicable" "no")]) (define_insn "arm_sync_old_nandsi" [(set (match_operand:SI 0 "s_register_operand" "=&r") (unspec_volatile:SI [(not:SI (and:SI (match_operand:SI 1 "arm_sync_memory_operand" "+Q") (match_operand:SI 2 "s_register_operand" "r"))) ] VUNSPEC_SYNC_OLD_OP)) (set (match_dup 1) (unspec_volatile:SI [(match_dup 1) (match_dup 2)] VUNSPEC_SYNC_OLD_OP)) (clobber (reg:CC CC_REGNUM)) (clobber (match_scratch:SI 3 "=&r")) (clobber (match_scratch:SI 4 "=&r"))] "TARGET_HAVE_LDREX && TARGET_HAVE_MEMORY_BARRIER" { return arm_output_sync_insn (insn, operands); } [(set_attr "sync_result" "0") (set_attr "sync_memory" "1") (set_attr "sync_new_value" "2") (set_attr "sync_t1" "3") (set_attr "sync_t2" "4") (set_attr "sync_op" "nand") (set_attr "conds" "clob") (set_attr "predicable" "no")]) (define_insn "arm_sync_old_" [(set (match_operand:SI 0 "s_register_operand" "=&r") (unspec_volatile:SI [(syncop:SI (zero_extend:SI (match_operand:NARROW 1 "arm_sync_memory_operand" "+Q")) (match_operand:SI 2 "s_register_operand" "r")) ] VUNSPEC_SYNC_OLD_OP)) (set (match_dup 1) (unspec_volatile:NARROW [(match_dup 1) (match_dup 2)] VUNSPEC_SYNC_OLD_OP)) (clobber (reg:CC CC_REGNUM)) (clobber (match_scratch:SI 3 "=&r")) (clobber (match_scratch:SI 4 ""))] "TARGET_HAVE_LDREXBHD && TARGET_HAVE_MEMORY_BARRIER" { return arm_output_sync_insn (insn, operands); } [(set_attr "sync_result" "0") (set_attr "sync_memory" "1") (set_attr "sync_new_value" "2") (set_attr "sync_t1" "3") (set_attr "sync_t2" "") (set_attr "sync_op" "") (set_attr "conds" "clob") (set_attr "predicable" "no")]) (define_insn "arm_sync_old_nand" [(set (match_operand:SI 0 "s_register_operand" "=&r") (unspec_volatile:SI [(not:SI (and:SI (zero_extend:SI (match_operand:NARROW 1 "arm_sync_memory_operand" "+Q")) (match_operand:SI 2 "s_register_operand" "r"))) ] VUNSPEC_SYNC_OLD_OP)) (set (match_dup 1) (unspec_volatile:NARROW [(match_dup 1) (match_dup 2)] VUNSPEC_SYNC_OLD_OP)) (clobber (reg:CC CC_REGNUM)) (clobber (match_scratch:SI 3 "=&r")) (clobber (match_scratch:SI 4 "=&r"))] "TARGET_HAVE_LDREXBHD && TARGET_HAVE_MEMORY_BARRIER" { return arm_output_sync_insn (insn, operands); } [(set_attr "sync_result" "0") (set_attr "sync_memory" "1") (set_attr "sync_new_value" "2") (set_attr "sync_t1" "3") (set_attr "sync_t2" "4") (set_attr "sync_op" "nand") (set_attr "conds" "clob") (set_attr "predicable" "no")]) (define_insn "*memory_barrier" [(set (match_operand:BLK 0 "" "") (unspec:BLK [(match_dup 0)] UNSPEC_MEMORY_BARRIER))] "TARGET_HAVE_MEMORY_BARRIER" { return arm_output_memory_barrier (operands); } [(set_attr "length" "4") (set_attr "conds" "unconditional") (set_attr "predicable" "no")])