# Copyright (C) 2011-2020 Apple Inc. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
# THE POSSIBILITY OF SUCH DAMAGE.

require "config"
require "arm"
require "arm64"
require "ast"
require "x86"
require "mips"
require "cloop"

begin
    require "arm64e"
rescue LoadError
end

BACKENDS =
    [
     "X86",
     "X86_WIN",
     "X86_64",
     "X86_64_WIN",
     "ARMv7",
     "ARM64",
     "ARM64E",
     "MIPS",
     "C_LOOP",
     "C_LOOP_WIN"
    ]

# Keep the set of working backends separate from the set of backends that might be
# supported. This is great because the BACKENDS list is almost like a reserved
# words list, in that it causes settings resolution to treat those words specially.
# Hence this lets us set aside the name of a backend we might want to support in
# the future while not actually supporting the backend yet.
WORKING_BACKENDS =
    [
     "X86",
     "X86_WIN",
     "X86_64",
     "X86_64_WIN",
     "ARMv7",
     "ARM64",
     "ARM64E",
     "MIPS",
     "C_LOOP",
     "C_LOOP_WIN"
    ]

BACKEND_PATTERN = Regexp.new('\\A(' + BACKENDS.join(')|(') + ')\\Z')

$allBackends = {}
$validBackends = {}
BACKENDS.each {
    | backend |
    $validBackends[backend] = true
    $allBackends[backend] = true
}

def canonicalizeBackendNames(backendNames)
    newBackendNames = []
    backendNames.each {
        | backendName |
        backendName = backendName.upcase
        if backendName =~ /ARM.*/
            backendName.sub!(/ARMV7([KS]?)(.*)/) { | _ | 'ARMv7' + $1.downcase + $2 }
            backendName = "ARM64" if backendName == "ARM64_32"
        end
        backendName = "X86" if backendName == "I386"
        newBackendNames << backendName
        newBackendNames << "ARMv7" if backendName.start_with?("ARMv7")
    }
    newBackendNames.uniq
end

def includeOnlyBackends(list)
    newValidBackends = {}
    list.each {
        | backend |
        if $validBackends[backend]
            newValidBackends[backend] = true
        end
    }
    $validBackends = newValidBackends
end

def isBackend?(backend)
    $allBackends[backend]
end

def isValidBackend?(backend)
    $validBackends[backend]
end

def validBackends
    $validBackends.keys
end

class LoweringError < StandardError
    attr_reader :originString
    
    def initialize(e, originString)
        super "#{e} (due to #{originString})"
        @originString = originString
        set_backtrace e.backtrace
    end
end

class Node
    def lower(name)
        begin
            $activeBackend = name
            send("prepareToLower", name)
            send("lower#{name}")
        rescue => e
            raise LoweringError.new(e, codeOriginString)
        end
    end
end

# Overrides for lower() for those nodes that are backend-agnostic

class Label
    def lower(name)
        $asm.debugAnnotation codeOrigin.debugDirective if $enableDebugAnnotations
        $asm.putsLabel(self.name[1..-1], @global)
    end
end

class LocalLabel
    def lower(name)
        $asm.putsLocalLabel "_offlineasm_#{self.name[1..-1]}"
    end
end

class LabelReference
    def asmLabel
        if extern?
            Assembler.externLabelReference(name[1..-1])
        else
            Assembler.labelReference(name[1..-1])
        end
    end

    def cLabel
        Assembler.cLabelReference(name[1..-1])
    end
end

class LocalLabelReference
    def asmLabel
        Assembler.localLabelReference("_offlineasm_"+name[1..-1])
    end

    def cLabel
        Assembler.cLocalLabelReference("_offlineasm_"+name[1..-1])
    end
end

class Skip
    def lower(name)
    end
end

class Sequence
    def lower(name)
        $activeBackend = name
        if respond_to? "getModifiedList#{name}"
            newList = send("getModifiedList#{name}")
            newList.each {
                | node |
                node.lower(name)
            }
        elsif respond_to? "lower#{name}"
            send("lower#{name}")
        else
            @list.each {
                | node |
                node.lower(name)
            }
        end
    end
end

